xDAI Price: $0.999971 (+0.01%)
Gas: 1.1 GWei

Contract

0x05F8Ad2ecdAD60d3Bfe44b3F5fc4A33076d0CEBc

Overview

xDAI Balance

Gnosis Chain LogoGnosis Chain LogoGnosis Chain Logo0 xDAI

xDAI Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Update Accumulat...375920302024-12-18 16:57:4558 days ago1734541065IN
0x05F8Ad2e...076d0CEBc
0 xDAI0.000152073.1

View more zero value Internal Transactions in Advanced View mode

Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
LendingManager

Compiler Version
v0.8.9+commit.e5eed63a

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 42 : LendingManager.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.9;

import {ILendingManager} from './interfaces/ILendingManager.sol';
import {ILendingModule} from './interfaces/ILendingModule.sol';
import {ILendingStorageManager} from './interfaces/ILendingStorageManager.sol';
import {ISynthereumFinder} from '../core/interfaces/IFinder.sol';
import {SynthereumInterfaces} from '../core/Constants.sol';
import {ISynthereumLendingTransfer} from '../synthereum-pool/common/interfaces/ILendingTransfer.sol';
import {ISynthereumLendingRewards} from '../synthereum-pool/common/interfaces/ILendingRewards.sol';
import {PreciseUnitMath} from '../base/utils/PreciseUnitMath.sol';
import {Address} from '../../@openzeppelin/contracts/utils/Address.sol';
import {IERC20} from '../../@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {SafeERC20} from '../../@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import {SynthereumFactoryAccess} from '../common/libs/FactoryAccess.sol';
import {AccessControlEnumerable} from '../../@openzeppelin/contracts/access/AccessControlEnumerable.sol';
import {ReentrancyGuard} from '../../@openzeppelin/contracts/security/ReentrancyGuard.sol';

contract LendingManager is
  ILendingManager,
  ReentrancyGuard,
  AccessControlEnumerable
{
  using Address for address;
  using SafeERC20 for IERC20;
  using PreciseUnitMath for uint256;

  ISynthereumFinder immutable synthereumFinder;

  bytes32 public constant MAINTAINER_ROLE = keccak256('Maintainer');

  string private constant DEPOSIT_SIG =
    'deposit((bytes32,uint256,uint256,uint256,address,uint64,address,uint64),bytes,uint256)';

  string private constant WITHDRAW_SIG =
    'withdraw((bytes32,uint256,uint256,uint256,address,uint64,address,uint64),address,bytes,uint256,address)';

  string private JRTSWAP_SIG =
    'swapToJRT(address,address,address,uint256,bytes)';

  string private TOTAL_TRANSFER_SIG =
    'totalTransfer(address,address,address,address,bytes)';

  modifier onlyMaintainer() {
    require(
      hasRole(MAINTAINER_ROLE, msg.sender),
      'Sender must be the maintainer'
    );
    _;
  }

  modifier onlyPoolFactory() {
    SynthereumFactoryAccess._onlyPoolFactory(synthereumFinder);
    _;
  }

  constructor(ISynthereumFinder _finder, Roles memory _roles) nonReentrant {
    synthereumFinder = _finder;

    _setRoleAdmin(DEFAULT_ADMIN_ROLE, DEFAULT_ADMIN_ROLE);
    _setRoleAdmin(MAINTAINER_ROLE, DEFAULT_ADMIN_ROLE);
    _setupRole(DEFAULT_ADMIN_ROLE, _roles.admin);
    _setupRole(MAINTAINER_ROLE, _roles.maintainer);
  }

  function deposit(uint256 _amount)
    external
    override
    nonReentrant
    returns (ReturnValues memory returnValues)
  {
    (
      ILendingStorageManager.PoolStorage memory poolData,
      ILendingStorageManager.LendingInfo memory lendingInfo,
      ILendingStorageManager poolStorageManager
    ) = _getPoolInfo();

    // delegate call implementation
    bytes memory result = address(lendingInfo.lendingModule)
      .functionDelegateCall(
      abi.encodeWithSignature(DEPOSIT_SIG, poolData, lendingInfo.args, _amount)
    );

    ILendingModule.ReturnValues memory res = abi.decode(
      result,
      (ILendingModule.ReturnValues)
    );

    // split interest
    InterestSplit memory interestSplit = splitGeneratedInterest(
      res.totalInterest,
      poolData.daoInterestShare,
      poolData.jrtBuybackShare
    );

    // update pool storage values
    poolStorageManager.updateValues(
      msg.sender,
      poolData.collateralDeposited + res.tokensOut + interestSplit.poolInterest,
      poolData.unclaimedDaoJRT + interestSplit.jrtInterest,
      poolData.unclaimedDaoCommission + interestSplit.commissionInterest
    );

    // set return values
    returnValues.tokensOut = res.tokensOut;
    returnValues.tokensTransferred = res.tokensTransferred;
    returnValues.poolInterest = interestSplit.poolInterest;
    returnValues.daoInterest =
      interestSplit.commissionInterest +
      interestSplit.jrtInterest;
    returnValues.prevTotalCollateral = poolData.collateralDeposited;
  }

  function withdraw(uint256 _interestTokenAmount, address _recipient)
    external
    override
    nonReentrant
    returns (ReturnValues memory returnValues)
  {
    (
      ILendingStorageManager.PoolStorage memory poolData,
      ILendingStorageManager.LendingInfo memory lendingInfo,
      ILendingStorageManager poolStorageManager
    ) = _getPoolInfo();

    // delegate call implementation
    bytes memory result = address(lendingInfo.lendingModule)
      .functionDelegateCall(
      abi.encodeWithSignature(
        WITHDRAW_SIG,
        poolData,
        msg.sender,
        lendingInfo.args,
        _interestTokenAmount,
        _recipient
      )
    );

    ILendingModule.ReturnValues memory res = abi.decode(
      result,
      (ILendingModule.ReturnValues)
    );

    // split interest
    InterestSplit memory interestSplit = splitGeneratedInterest(
      res.totalInterest,
      poolData.daoInterestShare,
      poolData.jrtBuybackShare
    );

    // update storage value
    poolStorageManager.updateValues(
      msg.sender,
      poolData.collateralDeposited + interestSplit.poolInterest - res.tokensOut,
      poolData.unclaimedDaoJRT + interestSplit.jrtInterest,
      poolData.unclaimedDaoCommission + interestSplit.commissionInterest
    );

    // set return values
    returnValues.tokensOut = res.tokensOut;
    returnValues.tokensTransferred = res.tokensTransferred;
    returnValues.poolInterest = interestSplit.poolInterest;
    returnValues.daoInterest =
      interestSplit.commissionInterest +
      interestSplit.jrtInterest;
    returnValues.prevTotalCollateral = poolData.collateralDeposited;
  }

  function updateAccumulatedInterest()
    external
    override
    nonReentrant
    returns (ReturnValues memory returnValues)
  {
    (
      ILendingStorageManager.PoolStorage memory poolData,
      ILendingStorageManager.LendingInfo memory lendingInfo,
      ILendingStorageManager poolStorageManager
    ) = _getPoolInfo();

    // retrieve accumulated interest
    uint256 totalInterest = ILendingModule(lendingInfo.lendingModule)
      .getUpdatedInterest(msg.sender, poolData, lendingInfo.args);

    // split according to shares
    InterestSplit memory interestSplit = splitGeneratedInterest(
      totalInterest,
      poolData.daoInterestShare,
      poolData.jrtBuybackShare
    );

    //update pool storage
    poolStorageManager.updateValues(
      msg.sender,
      poolData.collateralDeposited + interestSplit.poolInterest,
      poolData.unclaimedDaoJRT + interestSplit.jrtInterest,
      poolData.unclaimedDaoCommission + interestSplit.commissionInterest
    );

    // return values
    returnValues.poolInterest = interestSplit.poolInterest;
    returnValues.daoInterest =
      interestSplit.jrtInterest +
      interestSplit.commissionInterest;
    returnValues.prevTotalCollateral = poolData.collateralDeposited;
  }

  function batchClaimCommission(
    address[] calldata _pools,
    uint256[] calldata _amounts
  ) external override onlyMaintainer {
    require(_pools.length == _amounts.length, 'Invalid call');
    address recipient = synthereumFinder.getImplementationAddress(
      SynthereumInterfaces.CommissionReceiver
    );
    uint256 totalAmount;
    for (uint8 i = 0; i < _pools.length; i++) {
      if (_amounts[i] > 0) {
        claimCommission(_pools[i], _amounts[i], recipient);
        totalAmount += _amounts[i];
      }
    }

    emit BatchCommissionClaim(totalAmount, recipient);
  }

  function batchBuyback(
    address[] calldata _pools,
    uint256[] calldata _amounts,
    address _collateralAddress,
    bytes calldata _swapParams
  ) external override onlyMaintainer {
    require(_pools.length == _amounts.length, 'Invalid call');
    ILendingStorageManager poolStorageManager = getStorageManager();

    // withdraw collateral and update all pools
    uint256 aggregatedCollateral;
    address recipient = synthereumFinder.getImplementationAddress(
      SynthereumInterfaces.BuybackProgramReceiver
    );
    for (uint8 i = 0; i < _pools.length; i++) {
      address pool = _pools[i];
      uint256 _collateralAmount = _amounts[i];

      (
        ILendingStorageManager.PoolStorage memory poolData,
        ILendingStorageManager.LendingInfo memory lendingInfo
      ) = poolStorageManager.getPoolData(pool);

      // all pools need to have the same collateral
      require(poolData.collateral == _collateralAddress, 'Collateral mismatch');

      (uint256 interestTokenAmount, ) = collateralToInterestToken(
        pool,
        _collateralAmount
      );

      // trigger transfer of interest token from the pool
      interestTokenAmount = ISynthereumLendingTransfer(pool)
        .transferToLendingManager(interestTokenAmount);

      bytes memory withdrawRes = address(lendingInfo.lendingModule)
        .functionDelegateCall(
        abi.encodeWithSignature(
          WITHDRAW_SIG,
          poolData,
          pool,
          lendingInfo.args,
          interestTokenAmount,
          address(this)
        )
      );

      ILendingModule.ReturnValues memory res = abi.decode(
        withdrawRes,
        (ILendingModule.ReturnValues)
      );

      // update aggregated collateral to use for buyback
      aggregatedCollateral += res.tokensTransferred;

      // split interest
      InterestSplit memory interestSplit = splitGeneratedInterest(
        res.totalInterest,
        poolData.daoInterestShare,
        poolData.jrtBuybackShare
      );

      //update pool storage
      poolStorageManager.updateValues(
        pool,
        poolData.collateralDeposited + interestSplit.poolInterest,
        poolData.unclaimedDaoJRT + interestSplit.jrtInterest - res.tokensOut,
        poolData.unclaimedDaoCommission + interestSplit.commissionInterest
      );
    }

    // execute the buyback call with all the withdrawn collateral
    address JARVIS = synthereumFinder.getImplementationAddress(
      SynthereumInterfaces.JarvisToken
    );
    bytes memory result = address(
      poolStorageManager.getCollateralSwapModule(_collateralAddress)
    ).functionDelegateCall(
      abi.encodeWithSignature(
        JRTSWAP_SIG,
        recipient,
        _collateralAddress,
        JARVIS,
        aggregatedCollateral,
        _swapParams
      )
    );

    emit BatchBuyback(
      aggregatedCollateral,
      abi.decode(result, (uint256)),
      recipient
    );
  }

  function setLendingModule(
    string calldata _id,
    ILendingStorageManager.LendingInfo calldata _lendingInfo
  ) external override onlyMaintainer nonReentrant {
    ILendingStorageManager poolStorageManager = getStorageManager();
    poolStorageManager.setLendingModule(_id, _lendingInfo);
  }

  function addSwapProtocol(address _swapModule)
    external
    override
    onlyMaintainer
    nonReentrant
  {
    ILendingStorageManager poolStorageManager = getStorageManager();
    poolStorageManager.addSwapProtocol(_swapModule);
  }

  function removeSwapProtocol(address _swapModule)
    external
    override
    onlyMaintainer
    nonReentrant
  {
    ILendingStorageManager poolStorageManager = getStorageManager();
    poolStorageManager.removeSwapProtocol(_swapModule);
  }

  function setSwapModule(address _collateral, address _swapModule)
    external
    override
    onlyMaintainer
    nonReentrant
  {
    ILendingStorageManager poolStorageManager = getStorageManager();
    poolStorageManager.setSwapModule(_collateral, _swapModule);
  }

  function setShares(
    address _pool,
    uint64 _daoInterestShare,
    uint64 _jrtBuybackShare
  ) external override onlyMaintainer nonReentrant {
    ILendingStorageManager poolStorageManager = getStorageManager();
    poolStorageManager.setShares(_pool, _daoInterestShare, _jrtBuybackShare);
  }

  // to migrate liquidity to another lending module
  function migrateLendingModule(
    string memory _newLendingID,
    address _newInterestBearingToken,
    uint256 _interestTokenAmount
  ) external override nonReentrant returns (MigrateReturnValues memory) {
    (
      ILendingStorageManager.PoolStorage memory poolData,
      ILendingStorageManager.LendingInfo memory lendingInfo,
      ILendingStorageManager poolStorageManager
    ) = _getPoolInfo();

    uint256 prevDepositedCollateral = poolData.collateralDeposited;

    // delegate call withdraw collateral from old module
    ILendingModule.ReturnValues memory res;
    {
      bytes memory withdrawRes = address(lendingInfo.lendingModule)
        .functionDelegateCall(
        abi.encodeWithSignature(
          WITHDRAW_SIG,
          poolData,
          msg.sender,
          lendingInfo.args,
          _interestTokenAmount,
          address(this)
        )
      );

      res = abi.decode(withdrawRes, (ILendingModule.ReturnValues));
    }
    // split interest
    InterestSplit memory interestSplit = splitGeneratedInterest(
      res.totalInterest,
      poolData.daoInterestShare,
      poolData.jrtBuybackShare
    );

    // add interest to pool data
    uint256 newDaoJRT = poolData.unclaimedDaoJRT + interestSplit.jrtInterest;
    uint256 newDaoCommission = poolData.unclaimedDaoCommission +
      interestSplit.commissionInterest;

    // temporary set pool data collateral and interest to 0 to freshly deposit
    poolStorageManager.updateValues(msg.sender, 0, 0, 0);

    // set new lending module and obtain new pool data
    ILendingStorageManager.LendingInfo memory newLendingInfo;
    (poolData, newLendingInfo) = poolStorageManager.migrateLendingModule(
      _newLendingID,
      msg.sender,
      _newInterestBearingToken
    );

    // delegate call deposit into new module
    bytes memory result = address(newLendingInfo.lendingModule)
      .functionDelegateCall(
      abi.encodeWithSignature(
        DEPOSIT_SIG,
        poolData,
        newLendingInfo.args,
        res.tokensTransferred,
        msg.sender
      )
    );

    ILendingModule.ReturnValues memory depositRes = abi.decode(
      result,
      (ILendingModule.ReturnValues)
    );

    // update storage with accumulated interest
    uint256 actualCollateralDeposited = depositRes.tokensOut -
      newDaoJRT -
      newDaoCommission;

    poolStorageManager.updateValues(
      msg.sender,
      actualCollateralDeposited,
      newDaoJRT,
      newDaoCommission
    );

    return (
      MigrateReturnValues(
        prevDepositedCollateral,
        interestSplit.poolInterest,
        actualCollateralDeposited
      )
    );
  }

  function migratePool(address _migrationPool, address _newPool)
    external
    override
    onlyPoolFactory
    nonReentrant
    returns (uint256 sourceCollateralAmount, uint256 actualCollateralAmount)
  {
    ILendingStorageManager poolStorageManager = getStorageManager();
    (
      ILendingStorageManager.PoolLendingStorage memory lendingStorage,
      ILendingStorageManager.LendingInfo memory lendingInfo
    ) = poolStorageManager.getLendingData(_migrationPool);

    // delegate call deposit into new module
    bytes memory result = address(lendingInfo.lendingModule)
      .functionDelegateCall(
      abi.encodeWithSignature(
        TOTAL_TRANSFER_SIG,
        _migrationPool,
        _newPool,
        lendingStorage.collateralToken,
        lendingStorage.interestToken,
        lendingInfo.args
      )
    );

    (uint256 prevTotalAmount, uint256 newTotalAmount) = abi.decode(
      result,
      (uint256, uint256)
    );

    sourceCollateralAmount = poolStorageManager.getCollateralDeposited(
      _migrationPool
    );

    actualCollateralAmount =
      sourceCollateralAmount +
      newTotalAmount -
      prevTotalAmount;

    poolStorageManager.migratePoolStorage(
      _migrationPool,
      _newPool,
      actualCollateralAmount
    );
  }

  function claimLendingRewards(address[] calldata _pools)
    external
    override
    onlyMaintainer
    nonReentrant
  {
    ILendingStorageManager poolStorageManager = getStorageManager();
    ILendingStorageManager.PoolLendingStorage memory poolLendingStorage;
    ILendingStorageManager.LendingInfo memory lendingInfo;
    address recipient = synthereumFinder.getImplementationAddress(
      SynthereumInterfaces.LendingRewardsReceiver
    );
    for (uint8 i = 0; i < _pools.length; i++) {
      (poolLendingStorage, lendingInfo) = poolStorageManager.getLendingData(
        _pools[i]
      );
      ISynthereumLendingRewards(_pools[i]).claimLendingRewards(
        lendingInfo,
        poolLendingStorage,
        recipient
      );
    }
  }

  function interestTokenToCollateral(
    address _pool,
    uint256 _interestTokenAmount
  )
    external
    view
    override
    returns (uint256 collateralAmount, address interestTokenAddr)
  {
    ILendingStorageManager poolStorageManager = getStorageManager();
    (
      ILendingStorageManager.PoolLendingStorage memory lendingStorage,
      ILendingStorageManager.LendingInfo memory lendingInfo
    ) = poolStorageManager.getLendingData(_pool);

    collateralAmount = ILendingModule(lendingInfo.lendingModule)
      .interestTokenToCollateral(
      _interestTokenAmount,
      lendingStorage.collateralToken,
      lendingStorage.interestToken,
      lendingInfo.args
    );
    interestTokenAddr = lendingStorage.interestToken;
  }

  function getAccumulatedInterest(address _pool)
    external
    view
    override
    returns (
      uint256 poolInterest,
      uint256 commissionInterest,
      uint256 buybackInterest,
      uint256 collateralDeposited
    )
  {
    ILendingStorageManager poolStorageManager = getStorageManager();
    (
      ILendingStorageManager.PoolStorage memory poolData,
      ILendingStorageManager.LendingInfo memory lendingInfo
    ) = poolStorageManager.getPoolData(_pool);

    uint256 totalInterest = ILendingModule(lendingInfo.lendingModule)
      .getAccumulatedInterest(_pool, poolData, lendingInfo.args);

    InterestSplit memory interestSplit = splitGeneratedInterest(
      totalInterest,
      poolData.daoInterestShare,
      poolData.jrtBuybackShare
    );
    poolInterest = interestSplit.poolInterest;
    commissionInterest = interestSplit.commissionInterest;
    buybackInterest = interestSplit.jrtInterest;
    collateralDeposited = poolData.collateralDeposited;
  }

  function collateralToInterestToken(address _pool, uint256 _collateralAmount)
    public
    view
    override
    returns (uint256 interestTokenAmount, address interestTokenAddr)
  {
    ILendingStorageManager poolStorageManager = getStorageManager();
    (
      ILendingStorageManager.PoolLendingStorage memory lendingStorage,
      ILendingStorageManager.LendingInfo memory lendingInfo
    ) = poolStorageManager.getLendingData(_pool);

    interestTokenAmount = ILendingModule(lendingInfo.lendingModule)
      .collateralToInterestToken(
      _collateralAmount,
      lendingStorage.collateralToken,
      lendingStorage.interestToken,
      lendingInfo.args
    );
    interestTokenAddr = lendingStorage.interestToken;
  }

  function claimCommission(
    address _pool,
    uint256 _collateralAmount,
    address _recipient
  ) internal {
    ILendingStorageManager poolStorageManager = getStorageManager();
    (
      ILendingStorageManager.PoolStorage memory poolData,
      ILendingStorageManager.LendingInfo memory lendingInfo
    ) = poolStorageManager.getPoolData(_pool);

    // trigger transfer of funds from _pool
    (uint256 interestTokenAmount, ) = collateralToInterestToken(
      _pool,
      _collateralAmount
    );
    interestTokenAmount = ISynthereumLendingTransfer(_pool)
      .transferToLendingManager(interestTokenAmount);

    // delegate call withdraw
    bytes memory result = address(lendingInfo.lendingModule)
      .functionDelegateCall(
      abi.encodeWithSignature(
        WITHDRAW_SIG,
        poolData,
        _pool,
        lendingInfo.args,
        interestTokenAmount,
        _recipient
      )
    );
    ILendingModule.ReturnValues memory res = abi.decode(
      result,
      (ILendingModule.ReturnValues)
    );

    // split interest
    InterestSplit memory interestSplit = splitGeneratedInterest(
      res.totalInterest,
      poolData.daoInterestShare,
      poolData.jrtBuybackShare
    );

    //update pool storage
    poolStorageManager.updateValues(
      _pool,
      poolData.collateralDeposited + interestSplit.poolInterest,
      poolData.unclaimedDaoJRT + interestSplit.jrtInterest,
      poolData.unclaimedDaoCommission +
        interestSplit.commissionInterest -
        res.tokensOut
    );
  }

  function _getPoolInfo()
    internal
    view
    returns (
      ILendingStorageManager.PoolStorage memory poolData,
      ILendingStorageManager.LendingInfo memory lendingInfo,
      ILendingStorageManager poolStorageManager
    )
  {
    poolStorageManager = getStorageManager();
    (poolData, lendingInfo) = poolStorageManager.getPoolData(msg.sender);
  }

  function getStorageManager() internal view returns (ILendingStorageManager) {
    return
      ILendingStorageManager(
        synthereumFinder.getImplementationAddress(
          SynthereumInterfaces.LendingStorageManager
        )
      );
  }

  function splitGeneratedInterest(
    uint256 _totalInterestGenerated,
    uint64 _daoRatio,
    uint64 _jrtRatio
  ) internal pure returns (InterestSplit memory interestSplit) {
    if (_totalInterestGenerated == 0) return interestSplit;

    uint256 daoInterest = _totalInterestGenerated.mul(_daoRatio);
    interestSplit.jrtInterest = daoInterest.mul(_jrtRatio);
    interestSplit.commissionInterest = daoInterest - interestSplit.jrtInterest;
    interestSplit.poolInterest = _totalInterestGenerated - daoInterest;
  }
}

File 2 of 42 : AccessControl.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address => bool) members;
        bytes32 adminRole;
    }

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role, _msgSender());
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     */
    function _checkRole(bytes32 role, address account) internal view {
        if (!hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        Strings.toHexString(uint160(account), 20),
                        " is missing role ",
                        Strings.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view override returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    function _grantRole(bytes32 role, address account) private {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    function _revokeRole(bytes32 role, address account) private {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}

File 3 of 42 : IAccessControl.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}

File 4 of 42 : Context.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

File 5 of 42 : Strings.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _HEX_SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }
}

File 6 of 42 : ERC165.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 7 of 42 : IERC165.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @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 8 of 42 : AccessControlEnumerable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IAccessControlEnumerable.sol";
import "./AccessControl.sol";
import "../utils/structs/EnumerableSet.sol";

/**
 * @dev Extension of {AccessControl} that allows enumerating the members of each role.
 */
abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {
    using EnumerableSet for EnumerableSet.AddressSet;

    mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers;

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) public view override returns (address) {
        return _roleMembers[role].at(index);
    }

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) public view override returns (uint256) {
        return _roleMembers[role].length();
    }

    /**
     * @dev Overload {grantRole} to track enumerable memberships
     */
    function grantRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
        super.grantRole(role, account);
        _roleMembers[role].add(account);
    }

    /**
     * @dev Overload {revokeRole} to track enumerable memberships
     */
    function revokeRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
        super.revokeRole(role, account);
        _roleMembers[role].remove(account);
    }

    /**
     * @dev Overload {renounceRole} to track enumerable memberships
     */
    function renounceRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
        super.renounceRole(role, account);
        _roleMembers[role].remove(account);
    }

    /**
     * @dev Overload {_setupRole} to track enumerable memberships
     */
    function _setupRole(bytes32 role, address account) internal virtual override {
        super._setupRole(role, account);
        _roleMembers[role].add(account);
    }
}

File 9 of 42 : IAccessControlEnumerable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IAccessControl.sol";

/**
 * @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
 */
interface IAccessControlEnumerable is IAccessControl {
    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) external view returns (address);

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) external view returns (uint256);
}

File 10 of 42 : EnumerableSet.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                bytes32 lastvalue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastvalue;
                // Update the index for the moved value
                set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        return _values(set._inner);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        assembly {
            result := store
        }

        return result;
    }
}

File 11 of 42 : ILendingManager.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ILendingStorageManager} from './ILendingStorageManager.sol';

interface ILendingManager {
  struct Roles {
    address admin;
    address maintainer;
  }

  struct ReturnValues {
    uint256 poolInterest; //accumulated pool interest since last state-changing operation;
    uint256 daoInterest; //acccumulated dao interest since last state-changing operation;
    uint256 tokensOut; //amount of collateral used for a money market operation
    uint256 tokensTransferred; //amount of tokens finally transfered/received from money market (after eventual fees)
    uint256 prevTotalCollateral; //total collateral in the pool (users + LPs) before new operation
  }

  struct InterestSplit {
    uint256 poolInterest; // share of the total interest generated to the LPs;
    uint256 jrtInterest; // share of the total interest generated for jrt buyback;
    uint256 commissionInterest; // share of the total interest generated as dao commission;
  }

  struct MigrateReturnValues {
    uint256 prevTotalCollateral; // prevDepositedCollateral collateral deposited (without last interests) before the migration
    uint256 poolInterest; // poolInterests collateral interests accumalated before the migration
    uint256 actualTotalCollateral; // actualCollateralDeposited collateral deposited after the migration
  }

  event BatchBuyback(
    uint256 indexed collateralIn,
    uint256 JRTOut,
    address receiver
  );

  event BatchCommissionClaim(uint256 indexed collateralOut, address receiver);

  /**
   * @notice deposits collateral into the pool's associated money market
   * @dev calculates and return the generated interest since last state-changing operation
   * @param _collateralAmount amount of collateral to deposit
   * @return returnValues check struct
   */
  function deposit(uint256 _collateralAmount)
    external
    returns (ReturnValues memory returnValues);

  /**
   * @notice withdraw collateral from the pool's associated money market
   * @dev calculates and return the generated interest since last state-changing operation
   * @param _interestTokenAmount amount of interest tokens to redeem
   * @param _recipient the address receiving the collateral from money market
   * @return returnValues check struct
   */
  function withdraw(uint256 _interestTokenAmount, address _recipient)
    external
    returns (ReturnValues memory returnValues);

  /**
   * @notice calculate, split and update the generated interest of the caller pool since last state-changing operation
   * @return returnValues check struct
   */
  function updateAccumulatedInterest()
    external
    returns (ReturnValues memory returnValues);

  /**
   * @notice batches calls to redeem poolData.commissionInterest from multiple pools
   * @dev calculates and update the generated interest since last state-changing operation
   * @param _pools array of pools to redeem commissions from
   * @param _collateralAmounts array of amount of commission to redeem for each pool (matching pools order)
   */
  function batchClaimCommission(
    address[] calldata _pools,
    uint256[] calldata _collateralAmounts
  ) external;

  /**
   * @notice batches calls to redeem poolData.jrtInterest from multiple pools
   * @notice and executes a swap to buy Jarvis Reward Token
   * @dev calculates and update the generated interest since last state-changing operation
   * @param _pools array of pools to redeem collateral from
   * @param _collateralAmounts array of amount of commission to redeem for each pool (matching pools order)
   * @param _collateralAddress address of the pools collateral token (all pools must have the same collateral)
   * @param _swapParams encoded bytes necessary for the swap module
   */
  function batchBuyback(
    address[] calldata _pools,
    uint256[] calldata _collateralAmounts,
    address _collateralAddress,
    bytes calldata _swapParams
  ) external;

  /**
   * @notice sets the address of the implementation of a lending module and its extraBytes
   * @param _id associated to the lending module to be set
   * @param _lendingInfo see lendingInfo struct
   */
  function setLendingModule(
    string calldata _id,
    ILendingStorageManager.LendingInfo calldata _lendingInfo
  ) external;

  /**
   * @notice Add a swap module to the whitelist
   * @param _swapModule Swap module to add
   */
  function addSwapProtocol(address _swapModule) external;

  /**
   * @notice Remove a swap module from the whitelist
   * @param _swapModule Swap module to remove
   */
  function removeSwapProtocol(address _swapModule) external;

  /**
   * @notice sets an address as the swap module associated to a specific collateral
   * @dev the swapModule must implement the IJRTSwapModule interface
   * @param _collateral collateral address associated to the swap module
   * @param _swapModule IJRTSwapModule implementer contract
   */
  function setSwapModule(address _collateral, address _swapModule) external;

  /**
   * @notice set shares on interest generated by a pool collateral on the lending storage manager
   * @param _pool pool address to set shares on
   * @param _daoInterestShare share of total interest generated assigned to the dao
   * @param _jrtBuybackShare share of the total dao interest used to buyback jrt from an AMM
   */
  function setShares(
    address _pool,
    uint64 _daoInterestShare,
    uint64 _jrtBuybackShare
  ) external;

  /**
   * @notice migrates liquidity from one lending module (and money market), to a new one
   * @dev calculates and return the generated interest since last state-changing operation.
   * @dev The new lending module info must be have been previously set in the storage manager
   * @param _newLendingID id associated to the new lending module info
   * @param _newInterestBearingToken address of the interest token of the new money market
   * @param _interestTokenAmount total amount of interest token to migrate from old to new money market
   * @return migrateReturnValues check struct
   */
  function migrateLendingModule(
    string memory _newLendingID,
    address _newInterestBearingToken,
    uint256 _interestTokenAmount
  ) external returns (MigrateReturnValues memory);

  /**
   * @notice migrates pool storage from a deployed pool to a new pool
   * @param _migrationPool Pool from which the storage is migrated
   * @param _newPool address of the new pool
   * @return sourceCollateralAmount Collateral amount of the pool to migrate
   * @return actualCollateralAmount Collateral amount of the new deployed pool
   */
  function migratePool(address _migrationPool, address _newPool)
    external
    returns (uint256 sourceCollateralAmount, uint256 actualCollateralAmount);

  /**
   * @notice Claim leinding protocol rewards of a list of pools
   * @notice _pools List of pools from which claim rewards
   */
  function claimLendingRewards(address[] calldata _pools) external;

  /**
   * @notice returns the conversion between interest token and collateral of a specific money market
   * @param _pool reference pool to check conversion
   * @param _interestTokenAmount amount of interest token to calculate conversion on
   * @return collateralAmount amount of collateral after conversion
   * @return interestTokenAddr address of the associated interest token
   */
  function interestTokenToCollateral(
    address _pool,
    uint256 _interestTokenAmount
  ) external view returns (uint256 collateralAmount, address interestTokenAddr);

  /**
   * @notice returns accumulated interest of a pool since state-changing last operation
   * @dev does not update state
   * @param _pool reference pool to check accumulated interest
   * @return poolInterest amount of interest generated for the pool after splitting the dao share
   * @return commissionInterest amount of interest generated for the dao commissions
   * @return buybackInterest amount of interest generated for the buyback
   * @return collateralDeposited total amount of collateral currently deposited by the pool
   */
  function getAccumulatedInterest(address _pool)
    external
    view
    returns (
      uint256 poolInterest,
      uint256 commissionInterest,
      uint256 buybackInterest,
      uint256 collateralDeposited
    );

  /**
   * @notice returns the conversion between collateral and interest token of a specific money market
   * @param _pool reference pool to check conversion
   * @param _collateralAmount amount of collateral to calculate conversion on
   * @return interestTokenAmount amount of interest token after conversion
   * @return interestTokenAddr address of the associated interest token
   */
  function collateralToInterestToken(address _pool, uint256 _collateralAmount)
    external
    view
    returns (uint256 interestTokenAmount, address interestTokenAddr);
}

File 12 of 42 : ILendingModule.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ILendingStorageManager} from './ILendingStorageManager.sol';

interface ILendingModule {
  struct ReturnValues {
    uint256 totalInterest; // total accumulated interest of the pool since last state-changing operation
    uint256 tokensOut; //amount of tokens received from money market (before eventual fees)
    uint256 tokensTransferred; //amount of tokens finally transfered from money market (after eventual fees)
  }

  /**
   * @notice deposits collateral into the money market
   * @dev calculates and return the generated interest since last state-changing operation
   * @param _poolData pool storage information
   * @param _lendingArgs encoded args needed by the specific implementation
   * @param _amount of collateral to deposit
   * @return totalInterest check ReturnValues struct
   * @return tokensOut check ReturnValues struct
   * @return tokensTransferred check ReturnValues struct
   */
  function deposit(
    ILendingStorageManager.PoolStorage calldata _poolData,
    bytes calldata _lendingArgs,
    uint256 _amount
  )
    external
    returns (
      uint256 totalInterest,
      uint256 tokensOut,
      uint256 tokensTransferred
    );

  /**
   * @notice withdraw collateral from the money market
   * @dev calculates and return the generated interest since last state-changing operation
   * @param _poolData pool storage information
   * @param _pool pool address to calculate interest on
   * @param _lendingArgs encoded args needed by the specific implementation
   * @param _amount of interest tokens to redeem
   * @param _recipient address receiving the collateral from money market
   * @return totalInterest check ReturnValues struct
   * @return tokensOut check ReturnValues struct
   * @return tokensTransferred check ReturnValues struct
   */
  function withdraw(
    ILendingStorageManager.PoolStorage calldata _poolData,
    address _pool,
    bytes calldata _lendingArgs,
    uint256 _amount,
    address _recipient
  )
    external
    returns (
      uint256 totalInterest,
      uint256 tokensOut,
      uint256 tokensTransferred
    );

  /**
   * @notice transfer all interest token balance from an old pool to a new one
   * @param _oldPool Address of the old pool
   * @param _newPool Address of the new pool
   * @param _collateral address of collateral token
   * @param _interestToken address of interest token
   * @param _extraArgs encoded args the ILendingModule implementer might need. see ILendingManager.LendingInfo struct
   * @return prevTotalCollateral Total collateral in the old pool
   * @return actualTotalCollateral Total collateral in the new pool
   */
  function totalTransfer(
    address _oldPool,
    address _newPool,
    address _collateral,
    address _interestToken,
    bytes calldata _extraArgs
  )
    external
    returns (uint256 prevTotalCollateral, uint256 actualTotalCollateral);

  /**
   * @notice Claim the rewards associated to the bearing tokens of the caller(pool)
   * @param _lendingArgs encoded args needed by the specific implementation
   * @param _collateral Address of the collateral of the pool
   * @param _bearingToken Address of the bearing token of the pool
   * @param _recipient address to which send rewards
   */
  function claimRewards(
    bytes calldata _lendingArgs,
    address _collateral,
    address _bearingToken,
    address _recipient
  ) external;

  /**
   * @notice updates eventual state and returns updated accumulated interest
   * @param _poolAddress reference pool to check accumulated interest
   * @param _poolData pool storage information
   * @param _extraArgs encoded args the ILendingModule implementer might need. see ILendingManager.LendingInfo struct
   * @return totalInterest total amount of interest accumulated
   */
  function getUpdatedInterest(
    address _poolAddress,
    ILendingStorageManager.PoolStorage calldata _poolData,
    bytes calldata _extraArgs
  ) external returns (uint256 totalInterest);

  /**
   * @notice returns accumulated interest of a pool since state-changing last operation
   * @dev does not update state
   * @param _poolAddress reference pool to check accumulated interest
   * @param _poolData pool storage information
   * @param _extraArgs encoded args the ILendingModule implementer might need. see ILendingManager.LendingInfo struct
   * @return totalInterest total amount of interest accumulated
   */
  function getAccumulatedInterest(
    address _poolAddress,
    ILendingStorageManager.PoolStorage calldata _poolData,
    bytes calldata _extraArgs
  ) external view returns (uint256 totalInterest);

  /**
   * @notice returns bearing token associated to the collateral
   * @dev does not update state
   * @param _collateral collateral address to check bearing token
   * @param _extraArgs encoded args the ILendingModule implementer might need. see ILendingManager.LendingInfo struct
   * @return token bearing token
   */
  function getInterestBearingToken(
    address _collateral,
    bytes calldata _extraArgs
  ) external view returns (address token);

  /**
   * @notice returns the conversion between collateral and interest token of a specific money market
   * @param _collateralAmount amount of collateral to calculate conversion on
   * @param _collateral address of collateral token
   * @param _interestToken address of interest token
   * @param _extraArgs encoded args the ILendingModule implementer might need. see ILendingManager.LendingInfo struct
   * @return interestTokenAmount amount of interest token after conversion
   */
  function collateralToInterestToken(
    uint256 _collateralAmount,
    address _collateral,
    address _interestToken,
    bytes calldata _extraArgs
  ) external view returns (uint256 interestTokenAmount);

  /**
   * @notice returns the conversion between interest token and collateral of a specific money market
   * @param _interestTokenAmount amount of interest token to calculate conversion on
   * @param _collateral address of collateral token
   * @param _interestToken address of interest token
   * @param _extraArgs encoded args the ILendingModule implementer might need. see ILendingManager.LendingInfo struct
   * @return collateralAmount amount of collateral token after conversion
   */
  function interestTokenToCollateral(
    uint256 _interestTokenAmount,
    address _collateral,
    address _interestToken,
    bytes calldata _extraArgs
  ) external view returns (uint256 collateralAmount);
}

File 13 of 42 : ILendingStorageManager.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

interface ILendingStorageManager {
  struct PoolStorage {
    bytes32 lendingModuleId; // hash of the lending module id associated with the LendingInfo the pool currently is using
    uint256 collateralDeposited; // amount of collateral currently deposited in the MoneyMarket
    uint256 unclaimedDaoJRT; // amount of interest to be claimed to buyback JRT
    uint256 unclaimedDaoCommission; // amount of interest to be claimed as commission (in collateral)
    address collateral; // collateral address of the pool
    uint64 jrtBuybackShare; // share of dao interest used to buyback JRT
    address interestBearingToken; // interest token address of the pool
    uint64 daoInterestShare; // share of total interest generated by the pool directed to the DAO
  }

  struct PoolLendingStorage {
    address collateralToken; // address of the collateral token of a pool
    address interestToken; // address of interest token of a pool
  }

  struct LendingInfo {
    address lendingModule; // address of the ILendingModule interface implementer
    bytes args; // encoded args the ILendingModule implementer might need
  }

  /**
   * @notice sets a ILendingModule implementer info
   * @param _id string identifying a specific ILendingModule implementer
   * @param _lendingInfo see lendingInfo struct
   */
  function setLendingModule(
    string calldata _id,
    LendingInfo calldata _lendingInfo
  ) external;

  /**
   * @notice Add a swap module to the whitelist
   * @param _swapModule Swap module to add
   */
  function addSwapProtocol(address _swapModule) external;

  /**
   * @notice Remove a swap module from the whitelist
   * @param _swapModule Swap module to remove
   */
  function removeSwapProtocol(address _swapModule) external;

  /**
   * @notice sets an address as the swap module associated to a specific collateral
   * @dev the swapModule must implement the IJRTSwapModule interface
   * @param _collateral collateral address associated to the swap module
   * @param _swapModule IJRTSwapModule implementer contract
   */
  function setSwapModule(address _collateral, address _swapModule) external;

  /**
   * @notice set shares on interest generated by a pool collateral on the lending storage manager
   * @param _pool pool address to set shares on
   * @param _daoInterestShare share of total interest generated assigned to the dao
   * @param _jrtBuybackShare share of the total dao interest used to buyback jrt from an AMM
   */
  function setShares(
    address _pool,
    uint64 _daoInterestShare,
    uint64 _jrtBuybackShare
  ) external;

  /**
   * @notice store data for lending manager associated to a pool
   * @param _lendingID string identifying the associated ILendingModule implementer
   * @param _pool pool address to set info
   * @param _collateral collateral address of the pool
   * @param _interestBearingToken address of the interest token in use
   * @param _daoInterestShare share of total interest generated assigned to the dao
   * @param _jrtBuybackShare share of the total dao interest used to buyback jrt from an AMM
   */
  function setPoolStorage(
    string calldata _lendingID,
    address _pool,
    address _collateral,
    address _interestBearingToken,
    uint64 _daoInterestShare,
    uint64 _jrtBuybackShare
  ) external;

  /**
   * @notice assign oldPool storage information and state to newPool address and deletes oldPool storage slot
   * @dev is used when a pool is redeployed and the liquidity transferred over
   * @param _oldPool address of old pool to migrate storage from
   * @param _newPool address of the new pool receiving state of oldPool
   * @param _newCollateralDeposited Amount of collateral deposited in the new pool after the migration
   */
  function migratePoolStorage(
    address _oldPool,
    address _newPool,
    uint256 _newCollateralDeposited
  ) external;

  /**
   * @notice sets new lending info on a pool
   * @dev used when migrating liquidity from one lending module (and money market), to a new one
   * @dev The new lending module info must be have been previously set in the storage manager
   * @param _newLendingID id associated to the new lending module info
   * @param _pool address of the pool whose associated lending module is being migrated
   * @param _newInterestToken address of the interest token of the new Lending Module (can be set blank)
   * @return poolData with the updated state
   * @return lendingInfo of the new lending module
   */
  function migrateLendingModule(
    string calldata _newLendingID,
    address _pool,
    address _newInterestToken
  ) external returns (PoolStorage memory, LendingInfo memory);

  /**
   * @notice updates storage of a pool
   * @dev should be callable only by LendingManager after state-changing operations
   * @param _pool address of the pool to update values
   * @param _collateralDeposited updated amount of collateral deposited
   * @param _daoJRT updated amount of unclaimed interest for JRT buyback
   * @param _daoInterest updated amount of unclaimed interest as dao commission
   */
  function updateValues(
    address _pool,
    uint256 _collateralDeposited,
    uint256 _daoJRT,
    uint256 _daoInterest
  ) external;

  /**
   * @notice Returns info about a supported lending module
   * @param _id Name of the module
   * @return lendingInfo Address and bytes associated to the lending mdodule
   */
  function getLendingModule(string calldata _id)
    external
    view
    returns (LendingInfo memory lendingInfo);

  /**
   * @notice reads PoolStorage of a pool
   * @param _pool address of the pool to read storage
   * @return poolData pool struct info
   */
  function getPoolStorage(address _pool)
    external
    view
    returns (PoolStorage memory poolData);

  /**
   * @notice reads PoolStorage and LendingInfo of a pool
   * @param _pool address of the pool to read storage
   * @return poolData pool struct info
   * @return lendingInfo information of the lending module associated with the pool
   */
  function getPoolData(address _pool)
    external
    view
    returns (PoolStorage memory poolData, LendingInfo memory lendingInfo);

  /**
   * @notice reads lendingStorage and LendingInfo of a pool
   * @param _pool address of the pool to read storage
   * @return lendingStorage information of the addresses of collateral and intrestToken
   * @return lendingInfo information of the lending module associated with the pool
   */
  function getLendingData(address _pool)
    external
    view
    returns (
      PoolLendingStorage memory lendingStorage,
      LendingInfo memory lendingInfo
    );

  /**
   * @notice Return the list containing every swap module supported
   * @return List of swap modules
   */
  function getSwapModules() external view returns (address[] memory);

  /**
   * @notice reads the JRT Buyback module associated to a collateral
   * @param _collateral address of the collateral to retrieve module
   * @return swapModule address of interface implementer of the IJRTSwapModule
   */
  function getCollateralSwapModule(address _collateral)
    external
    view
    returns (address swapModule);

  /**
   * @notice reads the interest beaaring token address associated to a pool
   * @param _pool address of the pool to retrieve interest token
   * @return interestTokenAddr address of the interest token
   */
  function getInterestBearingToken(address _pool)
    external
    view
    returns (address interestTokenAddr);

  /**
   * @notice reads the shares used for splitting interests between pool, dao and buyback
   * @param _pool address of the pool to retrieve interest token
   * @return jrtBuybackShare Percentage of interests claimable by th DAO
   * @return daoInterestShare Percentage of interests used for the buyback
   */
  function getShares(address _pool)
    external
    view
    returns (uint256 jrtBuybackShare, uint256 daoInterestShare);

  /**
   * @notice reads the last collateral amount deposited in the pool
   * @param _pool address of the pool to retrieve collateral amount
   * @return collateralAmount Amount of collateral deposited in the pool
   */
  function getCollateralDeposited(address _pool)
    external
    view
    returns (uint256 collateralAmount);
}

File 14 of 42 : IFinder.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/**
 * @title Provides addresses of the contracts implementing certain interfaces.
 */
interface ISynthereumFinder {
  /**
   * @notice Updates the address of the contract that implements `interfaceName`.
   * @param interfaceName bytes32 encoding of the interface name that is either changed or registered.
   * @param implementationAddress address of the deployed contract that implements the interface.
   */
  function changeImplementationAddress(
    bytes32 interfaceName,
    address implementationAddress
  ) external;

  /**
   * @notice Gets the address of the contract that implements the given `interfaceName`.
   * @param interfaceName queried interface.
   * @return implementationAddress Address of the deployed contract that implements the interface.
   */
  function getImplementationAddress(bytes32 interfaceName)
    external
    view
    returns (address);
}

File 15 of 42 : Constants.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.9;

/**
 * @title Stores common interface names used throughout Synthereum.
 */
library SynthereumInterfaces {
  bytes32 public constant Deployer = 'Deployer';
  bytes32 public constant PoolRegistry = 'PoolRegistry';
  bytes32 public constant SelfMintingRegistry = 'SelfMintingRegistry';
  bytes32 public constant FixedRateRegistry = 'FixedRateRegistry';
  bytes32 public constant VaultRegistry = 'VaultRegistry';
  bytes32 public constant StakingLPVaultRegistry = 'StakingLPVaultRegistry';
  bytes32 public constant FactoryVersioning = 'FactoryVersioning';
  bytes32 public constant Manager = 'Manager';
  bytes32 public constant TokenFactory = 'TokenFactory';
  bytes32 public constant CreditLineController = 'CreditLineController';
  bytes32 public constant CollateralWhitelist = 'CollateralWhitelist';
  bytes32 public constant IdentifierWhitelist = 'IdentifierWhitelist';
  bytes32 public constant LendingManager = 'LendingManager';
  bytes32 public constant LendingStorageManager = 'LendingStorageManager';
  bytes32 public constant CommissionReceiver = 'CommissionReceiver';
  bytes32 public constant BuybackProgramReceiver = 'BuybackProgramReceiver';
  bytes32 public constant LendingRewardsReceiver = 'LendingRewardsReceiver';
  bytes32 public constant StakingRewardsReceiver = 'StakingRewardsReceiver';
  bytes32 public constant JarvisToken = 'JarvisToken';
  bytes32 public constant DebtTokenFactory = 'DebtTokenFactory';
  bytes32 public constant VaultFactory = 'VaultFactory';
  bytes32 public constant StakingLPVaultFactory = 'StakingLPVaultFactory';
  bytes32 public constant PriceFeed = 'PriceFeed';
  bytes32 public constant StakedJarvisToken = 'StakedJarvisToken';
  bytes32 public constant StakingLPVaultData = 'StakingLPVaultData';
  bytes32 public constant JarvisBrrrrr = 'JarvisBrrrrr';
  bytes32 public constant MoneyMarketManager = 'MoneyMarketManager';
  bytes32 public constant CrossChainBridge = 'CrossChainBridge';
  bytes32 public constant TrustedForwarder = 'TrustedForwarder';
}

library FactoryInterfaces {
  bytes32 public constant PoolFactory = 'PoolFactory';
  bytes32 public constant SelfMintingFactory = 'SelfMintingFactory';
  bytes32 public constant FixedRateFactory = 'FixedRateFactory';
}

File 16 of 42 : ILendingTransfer.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/**
 * @title Pool interface for making lending manager interacting with the pool
 */
interface ISynthereumLendingTransfer {
  /**
   * @notice Transfer a bearing amount to the lending manager
   * @notice Only the lending manager can call the function
   * @param _bearingAmount Amount of bearing token to transfer
   * @return bearingAmountOut Real bearing amount transferred to the lending manager
   */
  function transferToLendingManager(uint256 _bearingAmount)
    external
    returns (uint256 bearingAmountOut);
}

File 17 of 42 : ILendingRewards.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ILendingStorageManager} from '../../../lending-module/interfaces/ILendingStorageManager.sol';

/**
 * @title Pool interface for claiming lending rewards
 */
interface ISynthereumLendingRewards {
  /**
   * @notice Claim rewards, associaated to the lending module supported by the pool
   * @notice Only the lending manager can call the function
   * @param _lendingInfo Address of lending module implementation and global args
   * @param _poolLendingStorage Addresses of collateral and bearing token of the pool
   * @param _recipient Address of recipient receiving rewards
   */
  function claimLendingRewards(
    ILendingStorageManager.LendingInfo calldata _lendingInfo,
    ILendingStorageManager.PoolLendingStorage calldata _poolLendingStorage,
    address _recipient
  ) external;
}

File 18 of 42 : PreciseUnitMath.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.9;

/**
 * @title PreciseUnitMath
 * @author Synthereum Protocol
 *
 * Arithmetic for fixed-point numbers with 18 decimals of precision.
 *
 */
library PreciseUnitMath {
  // The number One in precise units.
  uint256 internal constant PRECISE_UNIT = 10**18;

  // The number One in precise units multiplied for 10^18.
  uint256 internal constant DOUBLE_PRECISE_UNIT = 10**36;

  // Max unsigned integer value
  uint256 internal constant MAX_UINT_256 = type(uint256).max;

  /**
   * @dev Getter function since constants can't be read directly from libraries.
   */
  function preciseUnit() internal pure returns (uint256) {
    return PRECISE_UNIT;
  }

  /**
   * @dev Getter function since constants can't be read directly from libraries.
   */
  function maxUint256() internal pure returns (uint256) {
    return MAX_UINT_256;
  }

  /**
   * @dev Multiplies value a by value b (result is rounded down). It's assumed that the value b is the significand
   * of a number with 18 decimals precision.
   */
  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
    return (a * b) / PRECISE_UNIT;
  }

  /**
   * @dev Multiplies value a by value b (result is rounded up). It's assumed that the value b is the significand
   * of a number with 18 decimals precision.
   */
  function mulCeil(uint256 a, uint256 b) internal pure returns (uint256) {
    if (a == 0 || b == 0) {
      return 0;
    }
    return (((a * b) - 1) / PRECISE_UNIT) + 1;
  }

  /**
   * @dev Divides value a by value b (result is rounded down).
   */
  function div(uint256 a, uint256 b) internal pure returns (uint256) {
    return (a * PRECISE_UNIT) / b;
  }

  /**
   * @dev Divides value a by value b (result is rounded up or away from 0).
   */
  function divCeil(uint256 a, uint256 b) internal pure returns (uint256) {
    require(b != 0, 'Cant divide by 0');

    return a > 0 ? (((a * PRECISE_UNIT) - 1) / b) + 1 : 0;
  }

  /**
   * @dev Performs the power on a specified value, reverts on overflow.
   */
  function safePower(uint256 a, uint256 pow) internal pure returns (uint256) {
    require(a > 0, 'Value must be positive');

    uint256 result = 1;
    for (uint256 i = 0; i < pow; i++) {
      uint256 previousResult = result;

      result = previousResult * a;
    }

    return result;
  }

  /**
   * @dev The minimum of `a` and `b`.
   */
  function min(uint256 a, uint256 b) internal pure returns (uint256) {
    return a < b ? a : b;
  }

  /**
   * @dev The maximum of `a` and `b`.
   */
  function max(uint256 a, uint256 b) internal pure returns (uint256) {
    return a > b ? a : b;
  }
}

File 19 of 42 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

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

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

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

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

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

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

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

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 20 of 42 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

    /**
     * @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);
}

File 21 of 42 : SafeERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

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

    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

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

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

File 22 of 42 : FactoryAccess.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.9;

import {ISynthereumFinder} from '../../core/interfaces/IFinder.sol';
import {ISynthereumFactoryVersioning} from '../../core/interfaces/IFactoryVersioning.sol';
import {SynthereumInterfaces, FactoryInterfaces} from '../../core/Constants.sol';

/** @title Library to use for controlling the access of a functions from the factories
 */
library SynthereumFactoryAccess {
  /**
   *@notice Revert if caller is not a Pool factory
   * @param _finder Synthereum finder
   */
  function _onlyPoolFactory(ISynthereumFinder _finder) internal view {

      ISynthereumFactoryVersioning factoryVersioning
     = ISynthereumFactoryVersioning(
      _finder.getImplementationAddress(SynthereumInterfaces.FactoryVersioning)
    );
    uint8 numberOfPoolFactories = factoryVersioning.numberOfFactoryVersions(
      FactoryInterfaces.PoolFactory
    );
    require(
      _checkSenderIsFactory(
        factoryVersioning,
        numberOfPoolFactories,
        FactoryInterfaces.PoolFactory
      ),
      'Not allowed'
    );
  }

  /**
   * @notice Revert if caller is not a Pool factory or a Fixed rate factory
   * @param _finder Synthereum finder
   */
  function _onlyPoolFactoryOrFixedRateFactory(ISynthereumFinder _finder)
    internal
    view
  {

      ISynthereumFactoryVersioning factoryVersioning
     = ISynthereumFactoryVersioning(
      _finder.getImplementationAddress(SynthereumInterfaces.FactoryVersioning)
    );
    uint8 numberOfPoolFactories = factoryVersioning.numberOfFactoryVersions(
      FactoryInterfaces.PoolFactory
    );
    uint8 numberOfFixedRateFactories = factoryVersioning
      .numberOfFactoryVersions(FactoryInterfaces.FixedRateFactory);
    bool isPoolFactory = _checkSenderIsFactory(
      factoryVersioning,
      numberOfPoolFactories,
      FactoryInterfaces.PoolFactory
    );
    if (isPoolFactory) {
      return;
    }
    bool isFixedRateFactory = _checkSenderIsFactory(
      factoryVersioning,
      numberOfFixedRateFactories,
      FactoryInterfaces.FixedRateFactory
    );
    if (isFixedRateFactory) {
      return;
    }
    revert('Sender must be a Pool or FixedRate factory');
  }

  /**
   * @notice Check if sender is a factory
   * @param _factoryVersioning SynthereumFactoryVersioning contract
   * @param _numberOfFactories Total number of versions of a factory type
   * @param _factoryKind Type of the factory
   * @return isFactory True if sender is a factory, otherwise false
   */
  function _checkSenderIsFactory(
    ISynthereumFactoryVersioning _factoryVersioning,
    uint8 _numberOfFactories,
    bytes32 _factoryKind
  ) private view returns (bool isFactory) {
    uint8 counterFactory;
    for (uint8 i = 0; counterFactory < _numberOfFactories; i++) {
      try _factoryVersioning.getFactoryVersion(_factoryKind, i) returns (
        address factory
      ) {
        if (msg.sender == factory) {
          isFactory = true;
          break;
        } else {
          counterFactory++;
          if (counterFactory == _numberOfFactories) {
            isFactory = false;
          }
        }
      } catch {}
    }
  }
}

File 23 of 42 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

File 24 of 42 : IFactoryVersioning.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/**
 * @title Provides addresses of different versions of pools factory and derivative factory
 */
interface ISynthereumFactoryVersioning {
  /** @notice Sets a Factory
   * @param factoryType Type of factory
   * @param version Version of the factory to be set
   * @param factory The pool factory address to be set
   */
  function setFactory(
    bytes32 factoryType,
    uint8 version,
    address factory
  ) external;

  /** @notice Removes a factory
   * @param factoryType The type of factory to be removed
   * @param version Version of the factory to be removed
   */
  function removeFactory(bytes32 factoryType, uint8 version) external;

  /** @notice Gets a factory contract address
   * @param factoryType The type of factory to be checked
   * @param version Version of the factory to be checked
   * @return factory Address of the factory contract
   */
  function getFactoryVersion(bytes32 factoryType, uint8 version)
    external
    view
    returns (address factory);

  /** @notice Gets the number of factory versions for a specific type
   * @param factoryType The type of factory to be checked
   * @return numberOfVersions Total number of versions for a specific factory
   */
  function numberOfFactoryVersions(bytes32 factoryType)
    external
    view
    returns (uint8 numberOfVersions);
}

File 25 of 42 : LendingStorageManager.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.9;

import {ISynthereumFinder} from '../core/interfaces/IFinder.sol';
import {ISynthereumFactoryVersioning} from '../core/interfaces/IFactoryVersioning.sol';
import {ILendingStorageManager} from './interfaces/ILendingStorageManager.sol';
import {ILendingModule} from './interfaces/ILendingModule.sol';
import {SynthereumInterfaces, FactoryInterfaces} from '../core/Constants.sol';
import {PreciseUnitMath} from '../base/utils/PreciseUnitMath.sol';
import {SynthereumFactoryAccess} from '../common/libs/FactoryAccess.sol';
import {EnumerableSet} from '../../@openzeppelin/contracts/utils/structs/EnumerableSet.sol';
import {ReentrancyGuard} from '../../@openzeppelin/contracts/security/ReentrancyGuard.sol';

contract LendingStorageManager is ILendingStorageManager, ReentrancyGuard {
  using EnumerableSet for EnumerableSet.AddressSet;

  mapping(bytes32 => LendingInfo) internal idToLendingInfo;
  EnumerableSet.AddressSet internal swapModules;
  mapping(address => address) internal collateralToSwapModule; // ie USDC -> JRTSwapUniswap address
  mapping(address => PoolStorage) internal poolStorage; // ie jEUR/USDC pooldata

  ISynthereumFinder immutable synthereumFinder;

  modifier onlyLendingManager() {
    require(
      msg.sender ==
        synthereumFinder.getImplementationAddress(
          SynthereumInterfaces.LendingManager
        ),
      'Not allowed'
    );
    _;
  }

  modifier onlyPoolFactory() {
    SynthereumFactoryAccess._onlyPoolFactory(synthereumFinder);
    _;
  }

  constructor(ISynthereumFinder _finder) {
    synthereumFinder = _finder;
  }

  function setLendingModule(
    string calldata _id,
    LendingInfo calldata _lendingInfo
  ) external override nonReentrant onlyLendingManager {
    bytes32 lendingId = keccak256(abi.encode(_id));
    require(lendingId != 0x00, 'Wrong module identifier');
    idToLendingInfo[lendingId] = _lendingInfo;
  }

  function addSwapProtocol(address _swapModule)
    external
    override
    nonReentrant
    onlyLendingManager
  {
    require(_swapModule != address(0), 'Swap module can not be 0x');
    require(swapModules.add(_swapModule), 'Swap module already supported');
  }

  function removeSwapProtocol(address _swapModule)
    external
    override
    nonReentrant
    onlyLendingManager
  {
    require(_swapModule != address(0), 'Swap module can not be 0x');
    require(swapModules.remove(_swapModule), 'Swap module not supported');
  }

  function setSwapModule(address _collateral, address _swapModule)
    external
    override
    nonReentrant
    onlyLendingManager
  {
    require(
      swapModules.contains(_swapModule) || _swapModule == address(0),
      'Swap module not supported'
    );
    collateralToSwapModule[_collateral] = _swapModule;
  }

  function setShares(
    address _pool,
    uint64 _daoInterestShare,
    uint64 _jrtBuybackShare
  ) external override nonReentrant onlyLendingManager {
    PoolStorage storage poolData = poolStorage[_pool];
    require(poolData.lendingModuleId != 0x00, 'Bad pool');
    require(
      _jrtBuybackShare <= PreciseUnitMath.PRECISE_UNIT &&
        _daoInterestShare <= PreciseUnitMath.PRECISE_UNIT,
      'Invalid share'
    );

    poolData.jrtBuybackShare = _jrtBuybackShare;
    poolData.daoInterestShare = _daoInterestShare;
  }

  function setPoolStorage(
    string calldata _lendingID,
    address _pool,
    address _collateral,
    address _interestBearingToken,
    uint64 _daoInterestShare,
    uint64 _jrtBuybackShare
  ) external override nonReentrant onlyPoolFactory {
    bytes32 id = keccak256(abi.encode(_lendingID));
    LendingInfo memory lendingInfo = idToLendingInfo[id];
    address lendingModule = lendingInfo.lendingModule;
    require(lendingModule != address(0), 'Module not supported');
    require(
      _jrtBuybackShare <= PreciseUnitMath.PRECISE_UNIT &&
        _daoInterestShare <= PreciseUnitMath.PRECISE_UNIT,
      'Invalid share'
    );

    // set pool storage
    PoolStorage storage poolData = poolStorage[_pool];
    require(poolData.lendingModuleId == 0x00, 'Pool already exists');
    poolData.lendingModuleId = id;
    poolData.collateral = _collateral;
    poolData.jrtBuybackShare = _jrtBuybackShare;
    poolData.daoInterestShare = _daoInterestShare;

    // set interest bearing token
    _setBearingToken(
      poolData,
      _collateral,
      lendingModule,
      lendingInfo,
      _interestBearingToken
    );
  }

  function migratePoolStorage(
    address _oldPool,
    address _newPool,
    uint256 _newCollateralDeposited
  ) external override nonReentrant onlyLendingManager {
    PoolStorage memory oldPoolData = poolStorage[_oldPool];
    bytes32 oldLendingId = oldPoolData.lendingModuleId;
    require(oldLendingId != 0x00, 'Bad migration pool');

    PoolStorage storage newPoolData = poolStorage[_newPool];
    require(newPoolData.lendingModuleId == 0x00, 'Bad new pool');

    // copy storage to new pool
    newPoolData.lendingModuleId = oldLendingId;
    newPoolData.collateral = oldPoolData.collateral;
    newPoolData.interestBearingToken = oldPoolData.interestBearingToken;
    newPoolData.jrtBuybackShare = oldPoolData.jrtBuybackShare;
    newPoolData.daoInterestShare = oldPoolData.daoInterestShare;
    newPoolData.collateralDeposited = _newCollateralDeposited;
    newPoolData.unclaimedDaoJRT = oldPoolData.unclaimedDaoJRT;
    newPoolData.unclaimedDaoCommission = oldPoolData.unclaimedDaoCommission;

    // delete old pool slot
    delete poolStorage[_oldPool];
  }

  function migrateLendingModule(
    string calldata _newLendingID,
    address _pool,
    address _newInterestBearingToken
  )
    external
    override
    nonReentrant
    onlyLendingManager
    returns (PoolStorage memory, LendingInfo memory)
  {
    bytes32 id = keccak256(abi.encode(_newLendingID));
    LendingInfo memory newLendingInfo = idToLendingInfo[id];
    address newLendingModule = newLendingInfo.lendingModule;
    require(newLendingModule != address(0), 'Id not existent');

    // set lending module
    PoolStorage storage poolData = poolStorage[_pool];
    poolData.lendingModuleId = id;

    // set interest bearing token
    _setBearingToken(
      poolData,
      poolData.collateral,
      newLendingModule,
      newLendingInfo,
      _newInterestBearingToken
    );

    return (poolData, newLendingInfo);
  }

  function updateValues(
    address _pool,
    uint256 _collateralDeposited,
    uint256 _daoJRT,
    uint256 _daoInterest
  ) external override nonReentrant onlyLendingManager {
    PoolStorage storage poolData = poolStorage[_pool];
    require(poolData.lendingModuleId != 0x00, 'Bad pool');

    // update collateral deposit amount of the pool
    poolData.collateralDeposited = _collateralDeposited;

    // update dao unclaimed interest of the pool
    poolData.unclaimedDaoJRT = _daoJRT;
    poolData.unclaimedDaoCommission = _daoInterest;
  }

  function getLendingModule(string calldata _id)
    external
    view
    override
    returns (LendingInfo memory lendingInfo)
  {
    bytes32 lendingId = keccak256(abi.encode(_id));
    require(lendingId != 0x00, 'Wrong module identifier');
    lendingInfo = idToLendingInfo[lendingId];
    require(
      lendingInfo.lendingModule != address(0),
      'Lending module not supported'
    );
  }

  function getPoolData(address _pool)
    external
    view
    override
    returns (PoolStorage memory poolData, LendingInfo memory lendingInfo)
  {
    poolData = poolStorage[_pool];
    require(poolData.lendingModuleId != 0x00, 'Not existing pool');
    lendingInfo = idToLendingInfo[poolData.lendingModuleId];
  }

  function getPoolStorage(address _pool)
    external
    view
    override
    returns (PoolStorage memory poolData)
  {
    poolData = poolStorage[_pool];
    require(poolData.lendingModuleId != 0x00, 'Not existing pool');
  }

  function getLendingData(address _pool)
    external
    view
    override
    returns (
      PoolLendingStorage memory lendingStorage,
      LendingInfo memory lendingInfo
    )
  {
    PoolStorage storage poolData = poolStorage[_pool];
    require(poolData.lendingModuleId != 0x00, 'Not existing pool');
    lendingStorage.collateralToken = poolData.collateral;
    lendingStorage.interestToken = poolData.interestBearingToken;
    lendingInfo = idToLendingInfo[poolData.lendingModuleId];
  }

  function getSwapModules() external view override returns (address[] memory) {
    uint256 numberOfModules = swapModules.length();
    address[] memory modulesList = new address[](numberOfModules);
    for (uint256 j = 0; j < numberOfModules; j++) {
      modulesList[j] = swapModules.at(j);
    }
    return modulesList;
  }

  function getCollateralSwapModule(address _collateral)
    external
    view
    override
    returns (address swapModule)
  {
    swapModule = collateralToSwapModule[_collateral];
    require(
      swapModule != address(0),
      'Swap module not added for this collateral'
    );
    require(swapModules.contains(swapModule), 'Swap module not supported');
  }

  function getInterestBearingToken(address _pool)
    external
    view
    override
    returns (address interestTokenAddr)
  {
    require(poolStorage[_pool].lendingModuleId != 0x00, 'Not existing pool');
    interestTokenAddr = poolStorage[_pool].interestBearingToken;
  }

  function getShares(address _pool)
    external
    view
    override
    returns (uint256 jrtBuybackShare, uint256 daoInterestShare)
  {
    require(poolStorage[_pool].lendingModuleId != 0x00, 'Not existing pool');
    jrtBuybackShare = poolStorage[_pool].jrtBuybackShare;
    daoInterestShare = poolStorage[_pool].daoInterestShare;
  }

  function getCollateralDeposited(address _pool)
    external
    view
    override
    returns (uint256 collateralAmount)
  {
    require(poolStorage[_pool].lendingModuleId != 0x00, 'Not existing pool');
    collateralAmount = poolStorage[_pool].collateralDeposited;
  }

  function _setBearingToken(
    PoolStorage storage _actualPoolData,
    address _collateral,
    address _lendingModule,
    LendingInfo memory _lendingInfo,
    address _interestToken
  ) internal {
    try
      ILendingModule(_lendingModule).getInterestBearingToken(
        _collateral,
        _lendingInfo.args
      )
    returns (address interestTokenAddr) {
      _actualPoolData.interestBearingToken = interestTokenAddr;
    } catch {
      require(_interestToken != address(0), 'No bearing token passed');
      _actualPoolData.interestBearingToken = _interestToken;
    }
  }
}

File 26 of 42 : PoolMigration.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.9;

import {ISynthereumFinder} from '../../../core/interfaces/IFinder.sol';
import {SynthereumFactoryAccess} from '../../../common/libs/FactoryAccess.sol';

/**
 * @title Abstract contract inherited by pools for moving storage from one pool to another
 */
contract SynthereumPoolMigration {
  ISynthereumFinder internal finder;

  modifier onlyPoolFactory() {
    SynthereumFactoryAccess._onlyPoolFactory(finder);
    _;
  }
}

File 27 of 42 : PoolMigrationFrom.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.9;

import {ISynthereumFinder} from '../../../core/interfaces/IFinder.sol';
import {SynthereumPoolMigration} from './PoolMigration.sol';

/**
 * @title Abstract contract inherit by pools for moving storage from one pool to another
 */
abstract contract SynthereumPoolMigrationFrom is SynthereumPoolMigration {
  /**
   * @notice Migrate storage from this pool resetting and cleaning data
   * @notice This can be called only by a pool factory
   * @return poolVersion Version of the pool
   * @return price Actual price of the pair
   * @return storageBytes Pool storage encoded in bytes
   */
  function migrateStorage()
    external
    virtual
    onlyPoolFactory
    returns (
      uint8 poolVersion,
      uint256 price,
      bytes memory storageBytes
    )
  {
    _modifyStorageFrom();
    (poolVersion, price, storageBytes) = _encodeStorage();
    _cleanStorage();
  }

  /**
   * @notice Transfer all bearing tokens to another address
   * @notice Only the lending manager can call the function
   * @param _recipient Address receving bearing amount
   * @return migrationAmount Total balance of the pool in bearing tokens before migration
   */
  function migrateTotalFunds(address _recipient)
    external
    virtual
    returns (uint256 migrationAmount);

  /**
   * @notice Function to implement for modifying storage before the encoding
   */
  function _modifyStorageFrom() internal virtual;

  /**
   * @notice Function to implement for cleaning and resetting the storage to the initial state
   */
  function _cleanStorage() internal virtual;

  /**
   * @notice Function to implement for encoding storage in bytes
   * @return poolVersion Version of the pool
   * @return price Actual price of the pair
   * @return storageBytes Pool storage encoded in bytes
   */
  function _encodeStorage()
    internal
    view
    virtual
    returns (
      uint8 poolVersion,
      uint256 price,
      bytes memory storageBytes
    );
}

File 28 of 42 : Transparent.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.9;

import {IERC20} from '../../../@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {ILendingModule} from '../interfaces/ILendingModule.sol';
import {ILendingStorageManager} from '../interfaces/ILendingStorageManager.sol';
import {SafeERC20} from '../../../@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import {SynthereumPoolMigrationFrom} from '../../synthereum-pool/common/migration/PoolMigrationFrom.sol';

// this module serves as a mock module
// it can be attached to any pool in order to avoid using a lending protocol
contract TransparentModule is ILendingModule {
  using SafeERC20 for IERC20;

  function deposit(
    ILendingStorageManager.PoolStorage calldata _poolData,
    bytes calldata,
    uint256 _amount
  )
    external
    override
    returns (
      uint256 totalInterest,
      uint256 tokensOut,
      uint256 tokensTransferred
    )
  {
    // transfer back collateral to the pool (msg.sender)
    IERC20 collateral = IERC20(_poolData.collateral);
    require(collateral.balanceOf(address(this)) >= _amount, 'Wrong balance');

    tokensTransferred = _amount;
    tokensOut = tokensTransferred;

    collateral.safeTransfer(msg.sender, tokensTransferred);
  }

  function withdraw(
    ILendingStorageManager.PoolStorage calldata _poolData,
    address,
    bytes calldata,
    uint256 _amount,
    address _recipient
  )
    external
    override
    returns (
      uint256 totalInterest,
      uint256 tokensOut,
      uint256 tokensTransferred
    )
  {
    // proxy should have received interest tokens (same as collateral in this case) from the pool
    tokensTransferred = _amount;
    tokensOut = tokensTransferred;

    // transfer collateral to recipient
    IERC20(_poolData.interestBearingToken).safeTransfer(
      _recipient,
      tokensTransferred
    );
  }

  function totalTransfer(
    address _oldPool,
    address _newPool,
    address,
    address,
    bytes calldata
  )
    external
    returns (uint256 prevTotalCollateral, uint256 actualTotalCollateral)
  {
    prevTotalCollateral = SynthereumPoolMigrationFrom(_oldPool)
      .migrateTotalFunds(_newPool);
    actualTotalCollateral = prevTotalCollateral;
  }

  function claimRewards(
    bytes calldata,
    address,
    address,
    address
  ) external pure override {
    revert('Claim rewards not supported');
  }

  function getUpdatedInterest(
    address,
    ILendingStorageManager.PoolStorage calldata,
    bytes calldata
  ) external pure override returns (uint256 totalInterest) {}

  function getAccumulatedInterest(
    address,
    ILendingStorageManager.PoolStorage calldata,
    bytes calldata
  ) external pure override returns (uint256 totalInterest) {}

  function getInterestBearingToken(address _collateral, bytes calldata)
    external
    pure
    override
    returns (address token)
  {
    token = _collateral;
  }

  function collateralToInterestToken(
    uint256 _collateralAmount,
    address,
    address,
    bytes calldata
  ) external pure override returns (uint256 interestTokenAmount) {
    interestTokenAmount = _collateralAmount;
  }

  function interestTokenToCollateral(
    uint256 _interestTokenAmount,
    address,
    address,
    bytes calldata
  ) external pure override returns (uint256 collateralAmount) {
    collateralAmount = _interestTokenAmount;
  }
}

File 29 of 42 : Univ2JRTSwap.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.9;

import {Address} from '../../../@openzeppelin/contracts/utils/Address.sol';
import {IERC20} from '../../../@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {SafeERC20} from '../../../@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import {IJRTSwapModule} from '../interfaces/IJrtSwapModule.sol';
import {IUniswapV2Router02} from '../../../@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol';

contract UniV2JRTSwapModule is IJRTSwapModule {
  using SafeERC20 for IERC20;

  struct SwapInfo {
    address routerAddress;
    address[] tokenSwapPath;
    uint256 expiration;
    uint256 minTokensOut;
  }

  function swapToJRT(
    address _recipient,
    address _collateral,
    address _jarvisToken,
    uint256 _amountIn,
    bytes calldata _params
  ) external override returns (uint256 amountOut) {
    // decode swapInfo
    SwapInfo memory swapInfo = abi.decode(_params, (SwapInfo));
    uint256 pathLength = swapInfo.tokenSwapPath.length;
    require(
      swapInfo.tokenSwapPath[pathLength - 1] == _jarvisToken,
      'Wrong token swap path'
    );

    // swap to JRT to final recipient
    IUniswapV2Router02 router = IUniswapV2Router02(swapInfo.routerAddress);

    IERC20(_collateral).safeIncreaseAllowance(address(router), _amountIn);
    amountOut = router.swapExactTokensForTokens(
      _amountIn,
      swapInfo.minTokensOut,
      swapInfo.tokenSwapPath,
      _recipient,
      swapInfo.expiration
    )[pathLength - 1];
  }
}

File 30 of 42 : IJrtSwapModule.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

interface IJRTSwapModule {
  /**
   * @notice executes an AMM swap from collateral to JRT
   * @param _recipient address receiving JRT tokens
   * @param _collateral address of the collateral token to swap
   * @param _jarvisToken address of the jarvis token to buy
   * @param _amountIn exact amount of collateral to swap
   * @param _params extra params needed on the specific implementation (with different AMM)
   * @return amountOut amount of JRT in output
   */
  function swapToJRT(
    address _recipient,
    address _collateral,
    address _jarvisToken,
    uint256 _amountIn,
    bytes calldata _params
  ) external returns (uint256 amountOut);
}

File 31 of 42 : IUniswapV2Router02.sol
pragma solidity >=0.6.2;

import './IUniswapV2Router01.sol';

interface IUniswapV2Router02 is IUniswapV2Router01 {
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountETH);
    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountETH);

    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external payable;
    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
}

File 32 of 42 : IUniswapV2Router01.sol
pragma solidity >=0.6.2;

interface IUniswapV2Router01 {
    function factory() external pure returns (address);
    function WETH() external pure returns (address);

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint amountADesired,
        uint amountBDesired,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB, uint liquidity);
    function addLiquidityETH(
        address token,
        uint amountTokenDesired,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external returns (uint amountA, uint amountB);
    function removeLiquidityETH(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountToken, uint amountETH);
    function removeLiquidityWithPermit(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountA, uint amountB);
    function removeLiquidityETHWithPermit(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountToken, uint amountETH);
    function swapExactTokensForTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapTokensForExactTokens(
        uint amountOut,
        uint amountInMax,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
    function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);
    function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);

    function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
    function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
    function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
    function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}

File 33 of 42 : BalancerJRTSwap.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.9;

import {Address} from '../../../@openzeppelin/contracts/utils/Address.sol';
import {IERC20} from '../../../@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {SafeERC20} from '../../../@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import {ISynthereumDeployment} from '../../common/interfaces/IDeployment.sol';
import {IBalancerVault} from '../interfaces/IBalancerVault.sol';
import {IJRTSwapModule} from '../interfaces/IJrtSwapModule.sol';

contract BalancerJRTSwapModule is IJRTSwapModule {
  using SafeERC20 for IERC20;

  struct SwapInfo {
    bytes32 poolId;
    address routerAddress;
    uint256 minTokensOut; // anti slippage
    uint256 expiration;
  }

  function swapToJRT(
    address _recipient,
    address _collateral,
    address _jarvisToken,
    uint256 _amountIn,
    bytes calldata _params
  ) external override returns (uint256 amountOut) {
    // decode swapInfo
    SwapInfo memory swapInfo = abi.decode(_params, (SwapInfo));

    // build params
    IBalancerVault.SingleSwap memory singleSwap = IBalancerVault.SingleSwap(
      swapInfo.poolId,
      IBalancerVault.SwapKind.GIVEN_IN,
      _collateral,
      _jarvisToken,
      _amountIn,
      '0x00'
    );

    IBalancerVault.FundManagement memory funds = IBalancerVault.FundManagement(
      address(this),
      false,
      payable(_recipient),
      false
    );

    // swap to JRT to final recipient
    IBalancerVault router = IBalancerVault(swapInfo.routerAddress);

    IERC20(_collateral).safeIncreaseAllowance(address(router), _amountIn);
    amountOut = router.swap(
      singleSwap,
      funds,
      swapInfo.minTokensOut,
      swapInfo.expiration
    );
  }
}

File 34 of 42 : IDeployment.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {IERC20} from '../../../@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {ISynthereumFinder} from '../../core/interfaces/IFinder.sol';

/**
 * @title Interface that a pool MUST have in order to be included in the deployer
 */
interface ISynthereumDeployment {
  /**
   * @notice Get Synthereum finder of the pool/self-minting derivative
   * @return finder Returns finder contract
   */
  function synthereumFinder() external view returns (ISynthereumFinder finder);

  /**
   * @notice Get Synthereum version
   * @return contractVersion Returns the version of this pool/self-minting derivative
   */
  function version() external view returns (uint8 contractVersion);

  /**
   * @notice Get the collateral token of this pool/self-minting derivative
   * @return collateralCurrency The ERC20 collateral token
   */
  function collateralToken() external view returns (IERC20 collateralCurrency);

  /**
   * @notice Get the synthetic token associated to this pool/self-minting derivative
   * @return syntheticCurrency The ERC20 synthetic token
   */
  function syntheticToken() external view returns (IERC20 syntheticCurrency);

  /**
   * @notice Get the synthetic token symbol associated to this pool/self-minting derivative
   * @return symbol The ERC20 synthetic token symbol
   */
  function syntheticTokenSymbol() external view returns (string memory symbol);
}

File 35 of 42 : IBalancerVault.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

interface IBalancerVault {
  enum SwapKind {
    GIVEN_IN,
    GIVEN_OUT
  }

  struct SingleSwap {
    bytes32 poolId;
    SwapKind kind;
    address assetIn;
    address assetOut;
    uint256 amount;
    bytes userData;
  }

  struct FundManagement {
    address sender;
    bool fromInternalBalance;
    address payable recipient;
    bool toInternalBalance;
  }

  function swap(
    SingleSwap memory singleSwap,
    FundManagement memory funds,
    uint256 limit,
    uint256 deadline
  ) external payable returns (uint256);
}

File 36 of 42 : Compound.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.9;

import {IERC20} from '../../../@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {ILendingModule} from '../interfaces/ILendingModule.sol';
import {ILendingStorageManager} from '../interfaces/ILendingStorageManager.sol';
import {ICompoundToken, IComptroller} from '../interfaces/ICToken.sol';
import {ExponentialNoError} from '../libs/ExponentialNoError.sol';
import {IRewardsController} from '../interfaces/IRewardsController.sol';
import {Address} from '../../../@openzeppelin/contracts/utils/Address.sol';
import {SafeERC20} from '../../../@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import {PreciseUnitMath} from '../../base/utils/PreciseUnitMath.sol';
import {SynthereumPoolMigrationFrom} from '../../synthereum-pool/common/migration/PoolMigrationFrom.sol';

contract CompoundModule is ILendingModule, ExponentialNoError {
  using SafeERC20 for IERC20;
  using SafeERC20 for ICompoundToken;

  function deposit(
    ILendingStorageManager.PoolStorage calldata _poolData,
    bytes calldata,
    uint256 _amount
  )
    external
    override
    returns (
      uint256 totalInterest,
      uint256 tokensOut,
      uint256 tokensTransferred
    )
  {
    // proxy should have received collateral from the pool
    IERC20 collateral = IERC20(_poolData.collateral);
    require(collateral.balanceOf(address(this)) >= _amount, 'Wrong balance');

    // initialise compound interest token
    ICompoundToken cToken = ICompoundToken(_poolData.interestBearingToken);

    // get tokens balance before
    uint256 cTokenBalanceBefore = cToken.balanceOf(address(this));

    uint256 totalPrevDeposit;

    // calculate accrued interest since last operation
    (totalInterest, totalPrevDeposit) = calculateGeneratedInterest(
      msg.sender,
      _poolData,
      0,
      cToken,
      true
    );

    // approve and deposit underlying
    collateral.safeIncreaseAllowance(address(cToken), _amount);
    require(cToken.mint(_amount) == 0, 'Failed mint');

    uint256 cTokenBalanceAfter = cToken.balanceOf(address(this));

    // set return values
    tokensTransferred = cTokenBalanceAfter - cTokenBalanceBefore;

    // transfer cToken to pool
    cToken.transfer(msg.sender, tokensTransferred);

    tokensOut =
      cToken.balanceOfUnderlying(msg.sender) -
      totalPrevDeposit -
      totalInterest;
  }

  function withdraw(
    ILendingStorageManager.PoolStorage calldata _poolData,
    address _pool,
    bytes calldata,
    uint256 _cTokenAmount,
    address _recipient
  )
    external
    override
    returns (
      uint256 totalInterest,
      uint256 tokensOut,
      uint256 tokensTransferred
    )
  {
    // initialise compound interest token
    ICompoundToken cToken = ICompoundToken(_poolData.interestBearingToken);

    IERC20 collateralToken = IERC20(_poolData.collateral);
    uint256 totalPrevDeposit;

    // calculate accrued interest since last operation
    (totalInterest, totalPrevDeposit) = calculateGeneratedInterest(
      _pool,
      _poolData,
      _cTokenAmount,
      cToken,
      false
    );

    // get balances before redeeming
    uint256 collBalanceBefore = collateralToken.balanceOf(address(this));

    // redeem
    require(cToken.redeem(_cTokenAmount) == 0, 'Failed withdraw');

    // get balances after redeeming
    uint256 collBalanceAfter = collateralToken.balanceOf(address(this));

    // set return values
    tokensOut =
      totalPrevDeposit +
      totalInterest -
      cToken.balanceOfUnderlying(_pool);
    tokensTransferred = collBalanceAfter - collBalanceBefore;

    // transfer underlying
    collateralToken.safeTransfer(_recipient, tokensTransferred);
  }

  function totalTransfer(
    address _oldPool,
    address _newPool,
    address,
    address _interestToken,
    bytes calldata
  )
    external
    override
    returns (uint256 prevTotalCollateral, uint256 actualTotalCollateral)
  {
    uint256 prevTotalcTokens = SynthereumPoolMigrationFrom(_oldPool)
      .migrateTotalFunds(_newPool);

    Exp memory exchangeRate = Exp({
      mantissa: ICompoundToken(_interestToken).exchangeRateCurrent()
    });
    prevTotalCollateral = mul_ScalarTruncate(exchangeRate, prevTotalcTokens);

    actualTotalCollateral = ICompoundToken(_interestToken).balanceOfUnderlying(
      _newPool
    );
  }

  function claimRewards(
    bytes calldata,
    address,
    address,
    address
  ) external override {
    revert('Claim rewards not supported');
  }

  function getUpdatedInterest(
    address _poolAddress,
    ILendingStorageManager.PoolStorage calldata _poolData,
    bytes calldata
  ) external override returns (uint256 totalInterest) {
    // instantiate cToken
    ICompoundToken cToken = ICompoundToken(_poolData.interestBearingToken);

    // calculate collateral
    uint256 totCollateral = cToken.balanceOfUnderlying(_poolAddress);

    totalInterest =
      totCollateral -
      _poolData.collateralDeposited -
      _poolData.unclaimedDaoCommission -
      _poolData.unclaimedDaoJRT;
  }

  function getAccumulatedInterest(
    address _poolAddress,
    ILendingStorageManager.PoolStorage calldata _poolData,
    bytes calldata
  ) external view override returns (uint256 totalInterest) {
    ICompoundToken cToken = ICompoundToken(_poolData.interestBearingToken);

    (, uint256 tokenBalance, , uint256 excMantissa) = cToken.getAccountSnapshot(
      _poolAddress
    );
    Exp memory exchangeRate = Exp({mantissa: excMantissa});

    uint256 totCollateral = mul_ScalarTruncate(exchangeRate, tokenBalance);

    totalInterest =
      totCollateral -
      _poolData.collateralDeposited -
      _poolData.unclaimedDaoCommission -
      _poolData.unclaimedDaoJRT;
  }

  function getInterestBearingToken(
    address _collateral,
    bytes calldata _extraArgs
  ) external view override returns (address token) {
    IComptroller comptroller = IComptroller(abi.decode(_extraArgs, (address)));
    address[] memory markets = comptroller.getAllMarkets();

    for (uint256 i = 0; i < markets.length; i++) {
      try ICompoundToken(markets[i]).underlying() returns (address coll) {
        if (coll == _collateral) {
          token = markets[i];
          break;
        }
      } catch {}
    }
    require(token != address(0), 'Token not found');
  }

  function collateralToInterestToken(
    uint256 _collateralAmount,
    address,
    address _interestToken,
    bytes calldata
  ) external view override returns (uint256 interestTokenAmount) {
    uint256 excMantissa = ICompoundToken(_interestToken).exchangeRateStored();
    Exp memory exchangeRate = Exp({mantissa: excMantissa});

    return div_(_collateralAmount, exchangeRate);
  }

  function interestTokenToCollateral(
    uint256 _interestTokenAmount,
    address,
    address _interestToken,
    bytes calldata
  ) external view override returns (uint256 collateralAmount) {
    uint256 excMantissa = ICompoundToken(_interestToken).exchangeRateStored();
    Exp memory exchangeRate = Exp({mantissa: excMantissa});
    return mul_ScalarTruncate(exchangeRate, _interestTokenAmount);
  }

  function calculateGeneratedInterest(
    address _poolAddress,
    ILendingStorageManager.PoolStorage calldata _pool,
    uint256 _cTokenAmount,
    ICompoundToken _cToken,
    bool _isDeposit
  )
    internal
    returns (uint256 totalInterestGenerated, uint256 totalPrevDeposit)
  {
    // get cToken pool balance and rate
    Exp memory exchangeRate = Exp({mantissa: _cToken.exchangeRateCurrent()});
    uint256 cTokenBalancePool = _cToken.balanceOf(_poolAddress);

    // determine amount of collateral the pool had before this operation
    uint256 poolBalance = mul_ScalarTruncate(
      exchangeRate,
      _isDeposit ? cTokenBalancePool : cTokenBalancePool + _cTokenAmount
    );

    totalPrevDeposit =
      _pool.collateralDeposited +
      _pool.unclaimedDaoCommission +
      _pool.unclaimedDaoJRT;

    totalInterestGenerated = poolBalance - totalPrevDeposit;
  }
}

File 37 of 42 : ICToken.sol
// SPDX-License-Identifier: AGPL-3.0-only
import {IERC20} from '../../../@openzeppelin/contracts/token/ERC20/IERC20.sol';

pragma solidity ^0.8.9;

interface ICompoundToken is IERC20 {
  function mint(uint256) external returns (uint256);

  function borrow(uint256) external returns (uint256);

  function borrowBalanceCurrent(address account) external returns (uint256);

  function repayBorrow(uint256) external returns (uint256);

  function exchangeRateCurrent() external returns (uint256);

  function supplyRatePerBlock() external returns (uint256);

  function redeem(uint256) external returns (uint256);

  function redeemUnderlying(uint256) external returns (uint256);

  function balanceOfUnderlying(address owner) external returns (uint256);

  function getAccountSnapshot(address account)
    external
    view
    returns (
      uint256,
      uint256,
      uint256,
      uint256
    );

  function underlying() external view returns (address);

  function getOwner() external view returns (address);

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

interface IComptroller {
  function getAllMarkets() external view returns (address[] memory);
}

File 38 of 42 : ExponentialNoError.sol
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.9;

/**
 * @title Exponential module for storing fixed-precision decimals
 * @author Compound
 * @notice Exp is a struct which stores decimals with a fixed precision of 18 decimal places.
 *         Thus, if we wanted to store the 5.1, mantissa would store 5.1e18. That is:
 *         `Exp({mantissa: 5100000000000000000})`.
 */
contract ExponentialNoError {
  uint256 constant expScale = 1e18;
  uint256 constant doubleScale = 1e36;
  uint256 constant halfExpScale = expScale / 2;
  uint256 constant mantissaOne = expScale;

  struct Exp {
    uint256 mantissa;
  }

  struct Double {
    uint256 mantissa;
  }

  /**
   * @dev Truncates the given exp to a whole number value.
   *      For example, truncate(Exp{mantissa: 15 * expScale}) = 15
   */
  function truncate(Exp memory exp) internal pure returns (uint256) {
    // Note: We are not using careful math here as we're performing a division that cannot fail
    return exp.mantissa / expScale;
  }

  /**
   * @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer.
   */
  function mul_ScalarTruncate(Exp memory a, uint256 scalar)
    internal
    pure
    returns (uint256)
  {
    Exp memory product = mul_(a, scalar);
    return truncate(product);
  }

  /**
   * @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer.
   */
  function mul_ScalarTruncateAddUInt(
    Exp memory a,
    uint256 scalar,
    uint256 addend
  ) internal pure returns (uint256) {
    Exp memory product = mul_(a, scalar);
    return add_(truncate(product), addend);
  }

  /**
   * @dev Checks if first Exp is less than second Exp.
   */
  function lessThanExp(Exp memory left, Exp memory right)
    internal
    pure
    returns (bool)
  {
    return left.mantissa < right.mantissa;
  }

  /**
   * @dev Checks if left Exp <= right Exp.
   */
  function lessThanOrEqualExp(Exp memory left, Exp memory right)
    internal
    pure
    returns (bool)
  {
    return left.mantissa <= right.mantissa;
  }

  /**
   * @dev Checks if left Exp > right Exp.
   */
  function greaterThanExp(Exp memory left, Exp memory right)
    internal
    pure
    returns (bool)
  {
    return left.mantissa > right.mantissa;
  }

  /**
   * @dev returns true if Exp is exactly zero
   */
  function isZeroExp(Exp memory value) internal pure returns (bool) {
    return value.mantissa == 0;
  }

  function safe224(uint256 n, string memory errorMessage)
    internal
    pure
    returns (uint224)
  {
    require(n < 2**224, errorMessage);
    return uint224(n);
  }

  function safe32(uint256 n, string memory errorMessage)
    internal
    pure
    returns (uint32)
  {
    require(n < 2**32, errorMessage);
    return uint32(n);
  }

  function add_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {
    return Exp({mantissa: add_(a.mantissa, b.mantissa)});
  }

  function add_(Double memory a, Double memory b)
    internal
    pure
    returns (Double memory)
  {
    return Double({mantissa: add_(a.mantissa, b.mantissa)});
  }

  function add_(uint256 a, uint256 b) internal pure returns (uint256) {
    return a + b;
  }

  function sub_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {
    return Exp({mantissa: sub_(a.mantissa, b.mantissa)});
  }

  function sub_(Double memory a, Double memory b)
    internal
    pure
    returns (Double memory)
  {
    return Double({mantissa: sub_(a.mantissa, b.mantissa)});
  }

  function sub_(uint256 a, uint256 b) internal pure returns (uint256) {
    return a - b;
  }

  function mul_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {
    return Exp({mantissa: mul_(a.mantissa, b.mantissa) / expScale});
  }

  function mul_(Exp memory a, uint256 b) internal pure returns (Exp memory) {
    return Exp({mantissa: mul_(a.mantissa, b)});
  }

  function mul_(uint256 a, Exp memory b) internal pure returns (uint256) {
    return mul_(a, b.mantissa) / expScale;
  }

  function mul_(Double memory a, Double memory b)
    internal
    pure
    returns (Double memory)
  {
    return Double({mantissa: mul_(a.mantissa, b.mantissa) / doubleScale});
  }

  function mul_(Double memory a, uint256 b)
    internal
    pure
    returns (Double memory)
  {
    return Double({mantissa: mul_(a.mantissa, b)});
  }

  function mul_(uint256 a, Double memory b) internal pure returns (uint256) {
    return mul_(a, b.mantissa) / doubleScale;
  }

  function mul_(uint256 a, uint256 b) internal pure returns (uint256) {
    return a * b;
  }

  function div_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {
    return Exp({mantissa: div_(mul_(a.mantissa, expScale), b.mantissa)});
  }

  function div_(Exp memory a, uint256 b) internal pure returns (Exp memory) {
    return Exp({mantissa: div_(a.mantissa, b)});
  }

  function div_(uint256 a, Exp memory b) internal pure returns (uint256) {
    return div_(mul_(a, expScale), b.mantissa);
  }

  function div_(Double memory a, Double memory b)
    internal
    pure
    returns (Double memory)
  {
    return Double({mantissa: div_(mul_(a.mantissa, doubleScale), b.mantissa)});
  }

  function div_(Double memory a, uint256 b)
    internal
    pure
    returns (Double memory)
  {
    return Double({mantissa: div_(a.mantissa, b)});
  }

  function div_(uint256 a, Double memory b) internal pure returns (uint256) {
    return div_(mul_(a, doubleScale), b.mantissa);
  }

  function div_(uint256 a, uint256 b) internal pure returns (uint256) {
    return a / b;
  }

  function fraction(uint256 a, uint256 b)
    internal
    pure
    returns (Double memory)
  {
    return Double({mantissa: div_(mul_(a, doubleScale), b)});
  }
}

File 39 of 42 : IRewardsController.sol
// SPDX-License-Identifier: agpl-3.0
pragma solidity >0.8.0;

/**
 * @title IRewardsController
 * @author Aave
 * @notice Defines the basic interface for a Rewards Controller.
 */
interface IRewardsController {
  /**
   * @dev Claims all rewards for a user to the desired address, on all the assets of the pool, accumulating the pending rewards
   * @param assets The list of assets to check eligible distributions before claiming rewards
   * @param to The address that will be receiving the rewards
   * @return rewardsList List of addresses of the reward tokens
   * @return claimedAmounts List that contains the claimed amount per reward, following same order as "rewardList"
   **/
  function claimAllRewards(address[] calldata assets, address to)
    external
    returns (address[] memory rewardsList, uint256[] memory claimedAmounts);
}

File 40 of 42 : AaveV3.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.9;

import {IERC20} from '../../../@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {ILendingModule} from '../interfaces/ILendingModule.sol';
import {ILendingStorageManager} from '../interfaces/ILendingStorageManager.sol';
import {IPool} from '../interfaces/IAaveV3.sol';
import {IRewardsController} from '../interfaces/IRewardsController.sol';
import {Address} from '../../../@openzeppelin/contracts/utils/Address.sol';
import {SafeERC20} from '../../../@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import {PreciseUnitMath} from '../../base/utils/PreciseUnitMath.sol';
import {SynthereumPoolMigrationFrom} from '../../synthereum-pool/common/migration/PoolMigrationFrom.sol';

contract AaveV3Module is ILendingModule {
  using SafeERC20 for IERC20;

  function deposit(
    ILendingStorageManager.PoolStorage calldata _poolData,
    bytes calldata _lendingArgs,
    uint256 _amount
  )
    external
    override
    returns (
      uint256 totalInterest,
      uint256 tokensOut,
      uint256 tokensTransferred
    )
  {
    // calculate accrued interest since last operation
    (uint256 interest, uint256 poolBalance) = calculateGeneratedInterest(
      msg.sender,
      _poolData,
      _amount,
      true
    );

    // proxy should have received collateral from the pool
    IERC20 collateral = IERC20(_poolData.collateral);
    require(collateral.balanceOf(address(this)) >= _amount, 'Wrong balance');

    // aave deposit - approve
    (address moneyMarket, ) = abi.decode(_lendingArgs, (address, address));

    collateral.safeIncreaseAllowance(moneyMarket, _amount);
    IPool(moneyMarket).supply(
      address(collateral),
      _amount,
      msg.sender,
      uint16(0)
    );

    // aave tokens are usually 1:1 (but in some case there is dust-wei of rounding)
    uint256 netDeposit = IERC20(_poolData.interestBearingToken).balanceOf(
      msg.sender
    ) - poolBalance;

    totalInterest = interest;
    tokensOut = netDeposit;
    tokensTransferred = netDeposit;
  }

  function withdraw(
    ILendingStorageManager.PoolStorage calldata _poolData,
    address _pool,
    bytes calldata _lendingArgs,
    uint256 _aTokensAmount,
    address _recipient
  )
    external
    override
    returns (
      uint256 totalInterest,
      uint256 tokensOut,
      uint256 tokensTransferred
    )
  {
    // proxy should have received interest tokens from the pool
    IERC20 interestToken = IERC20(_poolData.interestBearingToken);

    uint256 withdrawAmount = PreciseUnitMath.min(
      interestToken.balanceOf(address(this)),
      _aTokensAmount + 1
    );

    // calculate accrued interest since last operation
    (totalInterest, ) = calculateGeneratedInterest(
      _pool,
      _poolData,
      _aTokensAmount,
      false
    );

    uint256 initialBalance = IERC20(_poolData.collateral).balanceOf(_recipient);

    // aave withdraw - approve
    (address moneyMarket, ) = abi.decode(_lendingArgs, (address, address));

    interestToken.safeIncreaseAllowance(moneyMarket, withdrawAmount);
    IPool(moneyMarket).withdraw(
      _poolData.collateral,
      withdrawAmount,
      _recipient
    );

    // aave tokens are usually 1:1 (but in some case there is dust-wei of rounding)
    uint256 netWithdrawal = IERC20(_poolData.collateral).balanceOf(_recipient) -
      initialBalance;

    tokensOut = _aTokensAmount;
    tokensTransferred = netWithdrawal;
  }

  function totalTransfer(
    address _oldPool,
    address _newPool,
    address,
    address _interestToken,
    bytes calldata
  )
    external
    returns (uint256 prevTotalCollateral, uint256 actualTotalCollateral)
  {
    prevTotalCollateral = SynthereumPoolMigrationFrom(_oldPool)
      .migrateTotalFunds(_newPool);
    actualTotalCollateral = IERC20(_interestToken).balanceOf(_newPool);
  }

  /**
   * @notice Claim the rewards associated to the bearing tokens of the caller(pool)
   * @param _lendingArgs encoded args needed by the specific implementation
   * @param _bearingToken Address of the bearing token of the pool
   * @param _recipient address to which send rewards
   */
  function claimRewards(
    bytes calldata _lendingArgs,
    address,
    address _bearingToken,
    address _recipient
  ) external override {
    (, address rewardsController) = abi.decode(
      _lendingArgs,
      (address, address)
    );
    address[] memory assets = new address[](1);
    assets[0] = _bearingToken;
    IRewardsController(rewardsController).claimAllRewards(assets, _recipient);
  }

  function getUpdatedInterest(
    address _poolAddress,
    ILendingStorageManager.PoolStorage calldata _poolData,
    bytes calldata
  ) external view override returns (uint256 totalInterest) {
    (totalInterest, ) = calculateGeneratedInterest(
      _poolAddress,
      _poolData,
      0,
      true
    );
  }

  function getAccumulatedInterest(
    address _poolAddress,
    ILendingStorageManager.PoolStorage calldata _poolData,
    bytes calldata
  ) external view override returns (uint256 totalInterest) {
    (totalInterest, ) = calculateGeneratedInterest(
      _poolAddress,
      _poolData,
      0,
      true
    );
  }

  function getInterestBearingToken(address _collateral, bytes calldata _args)
    external
    view
    override
    returns (address token)
  {
    (address moneyMarket, ) = abi.decode(_args, (address, address));
    token = IPool(moneyMarket).getReserveData(_collateral).aTokenAddress;
    require(token != address(0), 'Interest token not found');
  }

  function collateralToInterestToken(
    uint256 _collateralAmount,
    address,
    address,
    bytes calldata
  ) external pure override returns (uint256 interestTokenAmount) {
    interestTokenAmount = _collateralAmount;
  }

  function interestTokenToCollateral(
    uint256 _interestTokenAmount,
    address,
    address,
    bytes calldata
  ) external pure override returns (uint256 collateralAmount) {
    collateralAmount = _interestTokenAmount;
  }

  function calculateGeneratedInterest(
    address _poolAddress,
    ILendingStorageManager.PoolStorage calldata _pool,
    uint256 _amount,
    bool _isDeposit
  )
    internal
    view
    returns (uint256 totalInterestGenerated, uint256 poolBalance)
  {
    // get current pool total amount of collateral
    poolBalance = IERC20(_pool.interestBearingToken).balanceOf(_poolAddress);

    // the total interest is delta between current balance and lastBalance
    totalInterestGenerated = _isDeposit
      ? poolBalance -
        _pool.collateralDeposited -
        _pool.unclaimedDaoCommission -
        _pool.unclaimedDaoJRT
      : poolBalance +
        _amount -
        _pool.collateralDeposited -
        _pool.unclaimedDaoCommission -
        _pool.unclaimedDaoJRT;
  }
}

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

interface IPool {
  struct ReserveConfigurationMap {
    uint256 data;
  }

  struct ReserveData {
    //stores the reserve configuration
    ReserveConfigurationMap configuration;
    //the liquidity index. Expressed in ray
    uint128 liquidityIndex;
    //the current supply rate. Expressed in ray
    uint128 currentLiquidityRate;
    //variable borrow index. Expressed in ray
    uint128 variableBorrowIndex;
    //the current variable borrow rate. Expressed in ray
    uint128 currentVariableBorrowRate;
    //the current stable borrow rate. Expressed in ray
    uint128 currentStableBorrowRate;
    //timestamp of last update
    uint40 lastUpdateTimestamp;
    //the id of the reserve. Represents the position in the list of the active reserves
    uint16 id;
    //aToken address
    address aTokenAddress;
    //stableDebtToken address
    address stableDebtTokenAddress;
    //variableDebtToken address
    address variableDebtTokenAddress;
    //address of the interest rate strategy
    address interestRateStrategyAddress;
    //the current treasury balance, scaled
    uint128 accruedToTreasury;
    //the outstanding unbacked aTokens minted through the bridging feature
    uint128 unbacked;
    //the outstanding debt borrowed against this asset in isolation mode
    uint128 isolationModeTotalDebt;
  }

  /**
   * @notice Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
   * - E.g. User supplies 100 USDC and gets in return 100 aUSDC
   * @param _asset The address of the underlying asset to supply
   * @param _amount The amount to be supplied
   * @param _onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
   *   wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
   *   is a different wallet
   * @param _referralCode Code used to register the integrator originating the operation, for potential rewards.
   *   0 if the action is executed directly by the user, without any middle-man
   **/
  function supply(
    address _asset,
    uint256 _amount,
    address _onBehalfOf,
    uint16 _referralCode
  ) external;

  /**
   * @notice Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned
   * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC
   * @param _asset The address of the underlying asset to withdraw
   * @param _amount The underlying amount to be withdrawn
   *   - Send the value type(uint256).max in order to withdraw the whole aToken balance
   * @param _to The address that will receive the underlying, same as msg.sender if the user
   *   wants to receive it on his own wallet, or a different address if the beneficiary is a
   *   different wallet
   * @return The final amount withdrawn
   **/
  function withdraw(
    address _asset,
    uint256 _amount,
    address _to
  ) external returns (uint256);

  /**
   * @notice Returns the state and configuration of the reserve
   * @param _asset The address of the underlying asset of the reserve
   * @return The state and configuration data of the reserve
   **/
  function getReserveData(address _asset)
    external
    view
    returns (ReserveData memory);
}

File 42 of 42 : Finder.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.9;

import {ISynthereumFinder} from './interfaces/IFinder.sol';
import {AccessControlEnumerable} from '../../@openzeppelin/contracts/access/AccessControlEnumerable.sol';

/**
 * @title Provides addresses of contracts implementing certain interfaces.
 */
contract SynthereumFinder is ISynthereumFinder, AccessControlEnumerable {
  bytes32 public constant MAINTAINER_ROLE = keccak256('Maintainer');

  //Describe role structure
  struct Roles {
    address admin;
    address maintainer;
  }

  //----------------------------------------
  // Storage
  //----------------------------------------

  mapping(bytes32 => address) public interfacesImplemented;

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

  event InterfaceImplementationChanged(
    bytes32 indexed interfaceName,
    address indexed newImplementationAddress
  );

  //----------------------------------------
  // Modifiers
  //----------------------------------------

  modifier onlyMaintainer() {
    require(
      hasRole(MAINTAINER_ROLE, msg.sender),
      'Sender must be the maintainer'
    );
    _;
  }

  //----------------------------------------
  // Constructors
  //----------------------------------------

  constructor(Roles memory roles) {
    _setRoleAdmin(DEFAULT_ADMIN_ROLE, DEFAULT_ADMIN_ROLE);
    _setRoleAdmin(MAINTAINER_ROLE, DEFAULT_ADMIN_ROLE);
    _setupRole(DEFAULT_ADMIN_ROLE, roles.admin);
    _setupRole(MAINTAINER_ROLE, roles.maintainer);
  }

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

  /**
   * @notice Updates the address of the contract that implements `interfaceName`.
   * @param interfaceName bytes32 of the interface name that is either changed or registered.
   * @param implementationAddress address of the implementation contract.
   */
  function changeImplementationAddress(
    bytes32 interfaceName,
    address implementationAddress
  ) external override onlyMaintainer {
    interfacesImplemented[interfaceName] = implementationAddress;

    emit InterfaceImplementationChanged(interfaceName, implementationAddress);
  }

  /**
   * @notice Gets the address of the contract that implements the given `interfaceName`.
   * @param interfaceName queried interface.
   * @return implementationAddress Address of the defined interface.
   */
  function getImplementationAddress(bytes32 interfaceName)
    external
    view
    override
    returns (address)
  {
    address implementationAddress = interfacesImplemented[interfaceName];
    require(implementationAddress != address(0x0), 'Implementation not found');
    return implementationAddress;
  }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract ISynthereumFinder","name":"_finder","type":"address"},{"components":[{"internalType":"address","name":"admin","type":"address"},{"internalType":"address","name":"maintainer","type":"address"}],"internalType":"struct ILendingManager.Roles","name":"_roles","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"collateralIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"JRTOut","type":"uint256"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"}],"name":"BatchBuyback","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"collateralOut","type":"uint256"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"}],"name":"BatchCommissionClaim","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAINTAINER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_swapModule","type":"address"}],"name":"addSwapProtocol","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_pools","type":"address[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"address","name":"_collateralAddress","type":"address"},{"internalType":"bytes","name":"_swapParams","type":"bytes"}],"name":"batchBuyback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_pools","type":"address[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"batchClaimCommission","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_pools","type":"address[]"}],"name":"claimLendingRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_pool","type":"address"},{"internalType":"uint256","name":"_collateralAmount","type":"uint256"}],"name":"collateralToInterestToken","outputs":[{"internalType":"uint256","name":"interestTokenAmount","type":"uint256"},{"internalType":"address","name":"interestTokenAddr","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"deposit","outputs":[{"components":[{"internalType":"uint256","name":"poolInterest","type":"uint256"},{"internalType":"uint256","name":"daoInterest","type":"uint256"},{"internalType":"uint256","name":"tokensOut","type":"uint256"},{"internalType":"uint256","name":"tokensTransferred","type":"uint256"},{"internalType":"uint256","name":"prevTotalCollateral","type":"uint256"}],"internalType":"struct ILendingManager.ReturnValues","name":"returnValues","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_pool","type":"address"}],"name":"getAccumulatedInterest","outputs":[{"internalType":"uint256","name":"poolInterest","type":"uint256"},{"internalType":"uint256","name":"commissionInterest","type":"uint256"},{"internalType":"uint256","name":"buybackInterest","type":"uint256"},{"internalType":"uint256","name":"collateralDeposited","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_pool","type":"address"},{"internalType":"uint256","name":"_interestTokenAmount","type":"uint256"}],"name":"interestTokenToCollateral","outputs":[{"internalType":"uint256","name":"collateralAmount","type":"uint256"},{"internalType":"address","name":"interestTokenAddr","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_newLendingID","type":"string"},{"internalType":"address","name":"_newInterestBearingToken","type":"address"},{"internalType":"uint256","name":"_interestTokenAmount","type":"uint256"}],"name":"migrateLendingModule","outputs":[{"components":[{"internalType":"uint256","name":"prevTotalCollateral","type":"uint256"},{"internalType":"uint256","name":"poolInterest","type":"uint256"},{"internalType":"uint256","name":"actualTotalCollateral","type":"uint256"}],"internalType":"struct ILendingManager.MigrateReturnValues","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_migrationPool","type":"address"},{"internalType":"address","name":"_newPool","type":"address"}],"name":"migratePool","outputs":[{"internalType":"uint256","name":"sourceCollateralAmount","type":"uint256"},{"internalType":"uint256","name":"actualCollateralAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_swapModule","type":"address"}],"name":"removeSwapProtocol","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_id","type":"string"},{"components":[{"internalType":"address","name":"lendingModule","type":"address"},{"internalType":"bytes","name":"args","type":"bytes"}],"internalType":"struct ILendingStorageManager.LendingInfo","name":"_lendingInfo","type":"tuple"}],"name":"setLendingModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_pool","type":"address"},{"internalType":"uint64","name":"_daoInterestShare","type":"uint64"},{"internalType":"uint64","name":"_jrtBuybackShare","type":"uint64"}],"name":"setShares","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_collateral","type":"address"},{"internalType":"address","name":"_swapModule","type":"address"}],"name":"setSwapModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"updateAccumulatedInterest","outputs":[{"components":[{"internalType":"uint256","name":"poolInterest","type":"uint256"},{"internalType":"uint256","name":"daoInterest","type":"uint256"},{"internalType":"uint256","name":"tokensOut","type":"uint256"},{"internalType":"uint256","name":"tokensTransferred","type":"uint256"},{"internalType":"uint256","name":"prevTotalCollateral","type":"uint256"}],"internalType":"struct ILendingManager.ReturnValues","name":"returnValues","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_interestTokenAmount","type":"uint256"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"withdraw","outputs":[{"components":[{"internalType":"uint256","name":"poolInterest","type":"uint256"},{"internalType":"uint256","name":"daoInterest","type":"uint256"},{"internalType":"uint256","name":"tokensOut","type":"uint256"},{"internalType":"uint256","name":"tokensTransferred","type":"uint256"},{"internalType":"uint256","name":"prevTotalCollateral","type":"uint256"}],"internalType":"struct ILendingManager.ReturnValues","name":"returnValues","type":"tuple"}],"stateMutability":"nonpayable","type":"function"}]

610100604052603060a0818152906200459560c03980516200002a91600391602090910190620002a4565b50604051806060016040528060348152602001620045c56034913980516200005b91600491602090910190620002a4565b503480156200006957600080fd5b5060405162004619380380620046198339810160408190526200008c9162000363565b600260009081556001600160a01b038316608052620000ac90806200010a565b620000c8600080516020620045f983398151915260006200010a565b8051620000d89060009062000157565b620000fd600080516020620045f983398151915282602001516200015760201b60201c565b505060016000556200043e565b6000828152600160208190526040808320909101805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b6200016e82826200019a60201b620023ef1760201c565b600082815260026020908152604090912062000195918390620023fd620001aa821b17901c565b505050565b620001a68282620001ca565b5050565b6000620001c1836001600160a01b03841662000252565b90505b92915050565b60008281526001602090815260408083206001600160a01b038516845290915290205460ff16620001a65760008281526001602081815260408084206001600160a01b0386168086529252808420805460ff19169093179092559051339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b60008181526001830160205260408120546200029b57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155620001c4565b506000620001c4565b828054620002b29062000401565b90600052602060002090601f016020900481019282620002d6576000855562000321565b82601f10620002f157805160ff191683800117855562000321565b8280016001018555821562000321579182015b828111156200032157825182559160200191906001019062000304565b506200032f92915062000333565b5090565b5b808211156200032f576000815560010162000334565b6001600160a01b03811681146200036057600080fd5b50565b60008082840360608112156200037857600080fd5b835162000385816200034a565b92506040601f19820112156200039a57600080fd5b50604080519081016001600160401b0381118282101715620003cc57634e487b7160e01b600052604160045260246000fd5b6040526020840151620003df816200034a565b81526040840151620003f1816200034a565b6020820152919491935090915050565b600181811c908216806200041657607f821691505b602082108114156200043857634e487b7160e01b600052602260045260246000fd5b50919050565b6080516141186200047d6000396000818161079101528181610b4b015281816112f80152818161173301528181611c81015261260101526141186000f3fe608060405234801561001057600080fd5b506004361061018d5760003560e01c806391d14854116100de578063ca15c87311610097578063db456b6211610071578063db456b6214610431578063de3627c714610444578063eead906a14610457578063f87422541461045f57600080fd5b8063ca15c873146103f8578063d547741f1461040b578063d612ea331461041e57600080fd5b806391d148541461037457806399dce61b14610387578063a217fddf146103b7578063a2be10bd146103bf578063aa566a62146103d2578063b6b55f25146103e557600080fd5b8063366694131161014b5780636c924f2f116101255780636c924f2f146102db57806374e44a97146102ee578063772b7e97146103215780639010d07c1461034957600080fd5b8063366694131461028057806338f6c371146102b5578063658c9a09146102c857600080fd5b8062f714ce1461019257806301ffc9a7146101f0578063220ea31d14610213578063248a9ca3146102285780632f2ff15d1461025a57806336568abe1461026d575b600080fd5b6101a56101a03660046131a0565b610474565b6040516101e79190600060a082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015292915050565b60405180910390f35b6102036101fe3660046131d0565b6106ac565b60405190151581526020016101e7565b610226610221366004613286565b6106d7565b005b61024c610236366004613334565b6000908152600160208190526040909120015490565b6040519081526020016101e7565b6102266102683660046131a0565b610d23565b61022661027b3660046131a0565b610d4a565b61029361028e366004613405565b610d6c565b60408051825181526020808401519082015291810151908201526060016101e7565b6102266102c33660046134a1565b61115e565b6102266102d63660046134cf565b611235565b6102266102e9366004613525565b6114e1565b6103016102fc366004613570565b6115c9565b6040805194855260208501939093529183015260608201526080016101e7565b61033461032f3660046134a1565b61172b565b604080519283526020830191909152016101e7565b61035c61035736600461358d565b61198d565b6040516001600160a01b0390911681526020016101e7565b6102036103823660046131a0565b6119ac565b61039a6103953660046135af565b6119d7565b604080519283526001600160a01b039091166020830152016101e7565b61024c600081565b6102266103cd366004613570565b611b13565b6102266103e03660046135db565b611be2565b6101a56103f3366004613334565b611e0a565b61024c610406366004613334565b611fdd565b6102266104193660046131a0565b611ff4565b61039a61042c3660046135af565b611ffe565b61022661043f366004613570565b6120d7565b610226610452366004613646565b61216f565b6101a5612208565b61024c60008051602061400683398151915281565b6104a66040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b600260005414156104d25760405162461bcd60e51b81526004016104c9906136aa565b60405180910390fd5b6002600090815580806104e3612412565b925092509250600061057a6040518060a001604052806067815260200161402660679139853386602001518b8b6040516024016105249594939291906137a9565b60408051601f19818403018152908290529161053f916137fb565b6040519081900390206020820180516001600160e01b03166001600160e01b031990921691909117905284516001600160a01b0316906124f5565b90506000818060200190518101906105929190613817565b905060006105ad82600001518760e001518860a0015161251a565b9050836001600160a01b031663120b8e5033846020015184600001518a602001516105d89190613888565b6105e291906138a0565b84602001518a604001516105f69190613888565b85604001518b6060015161060a9190613888565b6040518563ffffffff1660e01b815260040161062994939291906138b7565b600060405180830381600087803b15801561064357600080fd5b505af1158015610657573d6000803e3d6000fd5b5050506020808401516040808b01919091528085015160608b015283518a5290830151908301516106889250613888565b60208089019190915295909501516080870152505060016000555091949350505050565b60006001600160e01b03198216635a05180f60e01b14806106d157506106d18261259c565b92915050565b6106ef600080516020614006833981519152336119ac565b61070b5760405162461bcd60e51b81526004016104c9906138dd565b8584146107495760405162461bcd60e51b815260206004820152600c60248201526b125b9d985b1a590818d85b1b60a21b60448201526064016104c9565b60006107536125d1565b6040516302abf57960e61b815275213abcb130b1b5a83937b3b930b6a932b1b2b4bb32b960511b600482015290915060009081906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063aafd5e409060240160206040518083038186803b1580156107d357600080fd5b505afa1580156107e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061080b9190613924565b905060005b60ff81168a1115610b245760008b8b8360ff1681811061083257610832613941565b90506020020160208101906108479190613570565b905060008a8a8460ff1681811061086057610860613941565b6040516313d21cdf60e01b81526001600160a01b038681166004830152602090920293909301359350600092839250908916906313d21cdf9060240160006040518083038186803b1580156108b457600080fd5b505afa1580156108c8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526108f091908101906139f7565b915091508a6001600160a01b031682608001516001600160a01b03161461094f5760405162461bcd60e51b8152602060048201526013602482015272086ded8d8c2e8cae4c2d840dad2e6dac2e8c6d606b1b60448201526064016104c9565b600061095b8585611ffe565b5060405163152a13cd60e31b8152600481018290529091506001600160a01b0386169063a9509e6890602401602060405180830381600087803b1580156109a157600080fd5b505af11580156109b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109d99190613abc565b90506000610a166040518060a0016040528060678152602001614026606791398588866020015186306040516024016105249594939291906137a9565b9050600081806020019051810190610a2e9190613817565b905080604001518a610a409190613888565b99506000610a5b82600001518760e001518860a0015161251a565b90508b6001600160a01b031663120b8e508983600001518960200151610a819190613888565b856020015185602001518b60400151610a9a9190613888565b610aa491906138a0565b85604001518b60600151610ab89190613888565b6040518563ffffffff1660e01b8152600401610ad794939291906138b7565b600060405180830381600087803b158015610af157600080fd5b505af1158015610b05573d6000803e3d6000fd5b5050505050505050505050508080610b1c90613ad5565b915050610810565b506040516302abf57960e61b81526a2530b93b34b9aa37b5b2b760a91b60048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063aafd5e409060240160206040518083038186803b158015610b9557600080fd5b505afa158015610ba9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bcd9190613924565b90506000610cbe6003848a85888c8c604051602401610bf196959493929190613b1e565b60408051601f198184030181529082905291610c0c91613b66565b60408051918290039091206020830180516001600160e01b03166001600160e01b0319909216919091179052516326a966c160e11b81526001600160a01b038b81166004830152881690634d52cd829060240160206040518083038186803b158015610c7757600080fd5b505afa158015610c8b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610caf9190613924565b6001600160a01b0316906124f5565b9050837f15bc7538235a26bbf90ff27a286d54cc1f91d523cbf888e6a739fd36621ec50d82806020019051810190610cf69190613abc565b604080519182526001600160a01b03871660208301520160405180910390a2505050505050505050505050565b610d2d8282612688565b6000828152600260205260409020610d4590826123fd565b505050565b610d5482826126af565b6000828152600260205260409020610d459082612729565b610d9060405180606001604052806000815260200160008152602001600081525090565b60026000541415610db35760405162461bcd60e51b81526004016104c9906136aa565b600260009081558080610dc4612412565b925092509250600083602001519050610df760405180606001604052806000815260200160008152602001600081525090565b6000610e886040518060a001604052806067815260200161402660679139873388602001518c30604051602401610e329594939291906137a9565b60408051601f198184030181529082905291610e4d916137fb565b6040519081900390206020820180516001600160e01b03166001600160e01b031990921691909117905286516001600160a01b0316906124f5565b905080806020019051810190610e9e9190613817565b9150506000610eba82600001518760e001518860a0015161251a565b9050600081602001518760400151610ed29190613888565b9050600082604001518860600151610eea9190613888565b604051630120b8e560e41b81529091506001600160a01b0387169063120b8e5090610f20903390600090819081906004016138b7565b600060405180830381600087803b158015610f3a57600080fd5b505af1158015610f4e573d6000803e3d6000fd5b50505050610f6d60408051808201909152600081526060602082015290565b866001600160a01b031663ee3b32698e338f6040518463ffffffff1660e01b8152600401610f9d93929190613c02565b600060405180830381600087803b158015610fb757600080fd5b505af1158015610fcb573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610ff391908101906139f7565b809250819a505050600061108e60405180608001604052806056815260200161408d605691398b84602001518960400151336040516024016110389493929190613c35565b60408051601f198184030181529082905291611053916137fb565b6040519081900390206020820180516001600160e01b03166001600160e01b031990921691909117905283516001600160a01b0316906124f5565b90506000818060200190518101906110a69190613817565b90506000848683602001516110bb91906138a0565b6110c591906138a0565b604051630120b8e560e41b81529091506001600160a01b038b169063120b8e50906110fa90339085908b908b906004016138b7565b600060405180830381600087803b15801561111457600080fd5b505af1158015611128573d6000803e3d6000fd5b5050604080516060810182529b8c52985160208c01525050958801959095525050600160005550929a9950505050505050505050565b611176600080516020614006833981519152336119ac565b6111925760405162461bcd60e51b81526004016104c9906138dd565b600260005414156111b55760405162461bcd60e51b81526004016104c9906136aa565b600260009081556111c46125d1565b6040516338f6c37160e01b81526001600160a01b0385811660048301528481166024830152919250908216906338f6c37190604401600060405180830381600087803b15801561121357600080fd5b505af1158015611227573d6000803e3d6000fd5b505060016000555050505050565b61124d600080516020614006833981519152336119ac565b6112695760405162461bcd60e51b81526004016104c9906138dd565b6002600054141561128c5760405162461bcd60e51b81526004016104c9906136aa565b6002600090815561129b6125d1565b60408051808201909152600080825260208201529091506040805180820190915260008152606060208201526040516302abf57960e61b8152752632b73234b733a932bbb0b93239a932b1b2b4bb32b960511b60048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063aafd5e409060240160206040518083038186803b15801561134257600080fd5b505afa158015611356573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061137a9190613924565b905060005b60ff811686111561122757846001600160a01b0316632155162388888460ff168181106113ae576113ae613941565b90506020020160208101906113c39190613570565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260240160006040518083038186803b15801561140257600080fd5b505afa158015611416573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261143e9190810190613c7b565b9094509250868660ff831681811061145857611458613941565b905060200201602081019061146d9190613570565b6001600160a01b0316634f9b84778486856040518463ffffffff1660e01b815260040161149c93929190613ce2565b600060405180830381600087803b1580156114b657600080fd5b505af11580156114ca573d6000803e3d6000fd5b5050505080806114d990613ad5565b91505061137f565b6114f9600080516020614006833981519152336119ac565b6115155760405162461bcd60e51b81526004016104c9906138dd565b600260005414156115385760405162461bcd60e51b81526004016104c9906136aa565b600260009081556115476125d1565b604051636c924f2f60e01b81526001600160a01b0386811660048301526001600160401b0380871660248401528516604483015291925090821690636c924f2f906064015b600060405180830381600087803b1580156115a657600080fd5b505af11580156115ba573d6000803e3d6000fd5b50506001600055505050505050565b60008060008060006115d96125d1565b6040516313d21cdf60e01b81526001600160a01b0388811660048301529192506000918291908416906313d21cdf9060240160006040518083038186803b15801561162357600080fd5b505afa158015611637573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261165f91908101906139f7565b91509150600081600001516001600160a01b03166317e386468a8585602001516040518463ffffffff1660e01b815260040161169d93929190613d3a565b60206040518083038186803b1580156116b557600080fd5b505afa1580156116c9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116ed9190613abc565b90506000611704828560e001518660a0015161251a565b805160408201516020928301519690920151909c919b509499509397509295505050505050565b6000806117577f000000000000000000000000000000000000000000000000000000000000000061273e565b6002600054141561177a5760405162461bcd60e51b81526004016104c9906136aa565b600260009081556117896125d1565b604051632155162360e01b81526001600160a01b03878116600483015291925060009182919084169063215516239060240160006040518083038186803b1580156117d357600080fd5b505afa1580156117e7573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261180f9190810190613c7b565b91509150600061185a6004898986600001518760200151876020015160405160240161183f959493929190613d6b565b60408051601f19818403018152908290529161105391613b66565b9050600080828060200190518101906118739190613db2565b604051637d9d518b60e01b81526001600160a01b038d8116600483015292945090925090871690637d9d518b9060240160206040518083038186803b1580156118bb57600080fd5b505afa1580156118cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118f39190613abc565b975081611900828a613888565b61190a91906138a0565b6040516340ec3c6160e11b81526001600160a01b038c811660048301528b8116602483015260448201839052919850908716906381d878c290606401600060405180830381600087803b15801561196057600080fd5b505af1158015611974573d6000803e3d6000fd5b5050505050505050505060016000819055509250929050565b60008281526002602052604081206119a590836128ac565b9392505050565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b60008060006119e46125d1565b604051632155162360e01b81526001600160a01b03878116600483015291925060009182919084169063215516239060240160006040518083038186803b158015611a2e57600080fd5b505afa158015611a42573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611a6a9190810190613c7b565b9150915080600001516001600160a01b0316633ca5578c878460000151856020015185602001516040518563ffffffff1660e01b8152600401611ab09493929190613dd6565b60206040518083038186803b158015611ac857600080fd5b505afa158015611adc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b009190613abc565b9450816020015193505050509250929050565b611b2b600080516020614006833981519152336119ac565b611b475760405162461bcd60e51b81526004016104c9906138dd565b60026000541415611b6a5760405162461bcd60e51b81526004016104c9906136aa565b60026000908155611b796125d1565b60405163a2be10bd60e01b81526001600160a01b0384811660048301529192509082169063a2be10bd906024015b600060405180830381600087803b158015611bc157600080fd5b505af1158015611bd5573d6000803e3d6000fd5b5050600160005550505050565b611bfa600080516020614006833981519152336119ac565b611c165760405162461bcd60e51b81526004016104c9906138dd565b828114611c545760405162461bcd60e51b815260206004820152600c60248201526b125b9d985b1a590818d85b1b60a21b60448201526064016104c9565b6040516302abf57960e61b81527121b7b6b6b4b9b9b4b7b72932b1b2b4bb32b960711b60048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063aafd5e409060240160206040518083038186803b158015611ccb57600080fd5b505afa158015611cdf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d039190613924565b90506000805b60ff8116861115611dc357600085858360ff16818110611d2b57611d2b613941565b905060200201351115611db157611d8787878360ff16818110611d5057611d50613941565b9050602002016020810190611d659190613570565b86868460ff16818110611d7a57611d7a613941565b90506020020135856128b8565b84848260ff16818110611d9c57611d9c613941565b9050602002013582611dae9190613888565b91505b80611dbb81613ad5565b915050611d09565b506040516001600160a01b038316815281907f47592ec9d9991a137422beab7c67c9dde0899a7b5cc3c7df0812979abf5be8ac9060200160405180910390a2505050505050565b611e3c6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b60026000541415611e5f5760405162461bcd60e51b81526004016104c9906136aa565b600260009081558080611e70612412565b9250925092506000611eac60405180608001604052806056815260200161408d6056913960208501516040516105249188918b90602401613e08565b9050600081806020019051810190611ec49190613817565b90506000611edf82600001518760e001518860a0015161251a565b9050836001600160a01b031663120b8e5033836000015185602001518a60200151611f0a9190613888565b611f149190613888565b84602001518a60400151611f289190613888565b85604001518b60600151611f3c9190613888565b6040518563ffffffff1660e01b8152600401611f5b94939291906138b7565b600060405180830381600087803b158015611f7557600080fd5b505af1158015611f89573d6000803e3d6000fd5b5050506020808401516040808b01919091528085015160608b015283518a529083015190830151611fba9250613888565b602080890191909152959095015160808701525050600160005550919392505050565b60008181526002602052604081206106d190612b00565b610d548282612b0a565b600080600061200b6125d1565b604051632155162360e01b81526001600160a01b03878116600483015291925060009182919084169063215516239060240160006040518083038186803b15801561205557600080fd5b505afa158015612069573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526120919190810190613c7b565b9150915080600001516001600160a01b031663156b8523878460000151856020015185602001516040518563ffffffff1660e01b8152600401611ab09493929190613dd6565b6120ef600080516020614006833981519152336119ac565b61210b5760405162461bcd60e51b81526004016104c9906138dd565b6002600054141561212e5760405162461bcd60e51b81526004016104c9906136aa565b6002600090815561213d6125d1565b604051636da2b5b160e11b81526001600160a01b0384811660048301529192509082169063db456b6290602401611ba7565b612187600080516020614006833981519152336119ac565b6121a35760405162461bcd60e51b81526004016104c9906138dd565b600260005414156121c65760405162461bcd60e51b81526004016104c9906136aa565b600260009081556121d56125d1565b60405163de3627c760e01b81529091506001600160a01b0382169063de3627c79061158c90879087908790600401613e3c565b61223a6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b6002600054141561225d5760405162461bcd60e51b81526004016104c9906136aa565b60026000908155808061226e612412565b925092509250600082600001516001600160a01b031663b108ba2c338686602001516040518463ffffffff1660e01b81526004016122ae93929190613d3a565b602060405180830381600087803b1580156122c857600080fd5b505af11580156122dc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123009190613abc565b90506000612317828660e001518760a0015161251a565b9050826001600160a01b031663120b8e50338360000151886020015161233d9190613888565b846020015189604001516123519190613888565b85604001518a606001516123659190613888565b6040518563ffffffff1660e01b815260040161238494939291906138b7565b600060405180830381600087803b15801561239e57600080fd5b505af11580156123b2573d6000803e3d6000fd5b5050825188525050604081015160208201516123ce9190613888565b60208088019190915294909401516080860152505060016000555090919050565b6123f98282612b31565b5050565b60006119a5836001600160a01b038416612b9c565b60408051610100810182526000808252602080830182905282840182905260608084018390526080840183905260a0840183905260c0840183905260e08401839052845180860190955291845283015290600061246d6125d1565b6040516313d21cdf60e01b81523360048201529091506001600160a01b038216906313d21cdf9060240160006040518083038186803b1580156124af57600080fd5b505afa1580156124c3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526124eb91908101906139f7565b9094909350909150565b60606119a58383604051806060016040528060278152602001613fdf60279139612beb565b61253e60405180606001604052806000815260200160008152602001600081525090565b83612548576119a5565b600061255d856001600160401b038616612cbf565b9050612572816001600160401b038516612cbf565b6020830181905261258390826138a0565b604083015261259281866138a0565b8252509392505050565b60006001600160e01b03198216637965db0b60e01b14806106d157506301ffc9a760e01b6001600160e01b03198316146106d1565b6040516302abf57960e61b8152742632b73234b733a9ba37b930b3b2a6b0b730b3b2b960591b60048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063aafd5e409060240160206040518083038186803b15801561264b57600080fd5b505afa15801561265f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126839190613924565b905090565b600082815260016020819052604090912001546126a58133612cde565b610d458383612b31565b6001600160a01b038116331461271f5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084016104c9565b6123f98282612d42565b60006119a5836001600160a01b038416612da9565b6040516302abf57960e61b815270466163746f727956657273696f6e696e6760781b60048201526000906001600160a01b0383169063aafd5e409060240160206040518083038186803b15801561279457600080fd5b505afa1580156127a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127cc9190613924565b604051636d2721a360e11b81526a506f6f6c466163746f727960a81b60048201529091506000906001600160a01b0383169063da4e43469060240160206040518083038186803b15801561281f57600080fd5b505afa158015612833573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128579190613ec5565b905061287282826a506f6f6c466163746f727960a81b612e9c565b610d455760405162461bcd60e51b815260206004820152600b60248201526a139bdd08185b1b1bddd95960aa1b60448201526064016104c9565b60006119a58383612f8a565b60006128c26125d1565b6040516313d21cdf60e01b81526001600160a01b0386811660048301529192506000918291908416906313d21cdf9060240160006040518083038186803b15801561290c57600080fd5b505afa158015612920573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261294891908101906139f7565b9150915060006129588787611ffe565b5060405163152a13cd60e31b8152600481018290529091506001600160a01b0388169063a9509e6890602401602060405180830381600087803b15801561299e57600080fd5b505af11580156129b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129d69190613abc565b90506000612a136040518060a001604052806067815260200161402660679139858a8660200151868b6040516024016105249594939291906137a9565b9050600081806020019051810190612a2b9190613817565b90506000612a4682600001518760e001518860a0015161251a565b9050866001600160a01b031663120b8e508b83600001518960200151612a6c9190613888565b84602001518a60400151612a809190613888565b866020015186604001518c60600151612a999190613888565b612aa391906138a0565b6040518563ffffffff1660e01b8152600401612ac294939291906138b7565b600060405180830381600087803b158015612adc57600080fd5b505af1158015612af0573d6000803e3d6000fd5b5050505050505050505050505050565b60006106d1825490565b60008281526001602081905260409091200154612b278133612cde565b610d458383612d42565b612b3b82826119ac565b6123f95760008281526001602081815260408084206001600160a01b0386168086529252808420805460ff19169093179092559051339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b6000818152600183016020526040812054612be3575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556106d1565b5060006106d1565b6060833b612c4a5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b60648201526084016104c9565b600080856001600160a01b031685604051612c6591906137fb565b600060405180830381855af49150503d8060008114612ca0576040519150601f19603f3d011682016040523d82523d6000602084013e612ca5565b606091505b5091509150612cb5828286612fb4565b9695505050505050565b6000670de0b6b3a7640000612cd48385613ee8565b6119a59190613f07565b612ce882826119ac565b6123f957612d00816001600160a01b03166014612fed565b612d0b836020612fed565b604051602001612d1c929190613f29565b60408051601f198184030181529082905262461bcd60e51b82526104c991600401613f9e565b612d4c82826119ac565b156123f95760008281526001602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60008181526001830160205260408120548015612e92576000612dcd6001836138a0565b8554909150600090612de1906001906138a0565b9050818114612e46576000866000018281548110612e0157612e01613941565b9060005260206000200154905080876000018481548110612e2457612e24613941565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080612e5757612e57613fb1565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506106d1565b60009150506106d1565b60008060005b8460ff168260ff161015612f8157604051636839980160e11b81526004810185905260ff821660248201526001600160a01b0387169063d07330029060440160206040518083038186803b158015612ef957600080fd5b505afa925050508015612f29575060408051601f3d908101601f19168201909252612f2691810190613924565b60015b612f3257612f6f565b336001600160a01b0382161415612f4d576001935050612f81565b82612f5781613ad5565b9350508560ff168360ff161415612f6d57600093505b505b80612f7981613ad5565b915050612ea2565b50509392505050565b6000826000018281548110612fa157612fa1613941565b9060005260206000200154905092915050565b60608315612fc35750816119a5565b825115612fd35782518084602001fd5b8160405162461bcd60e51b81526004016104c99190613f9e565b60606000612ffc836002613ee8565b613007906002613888565b6001600160401b0381111561301e5761301e61334d565b6040519080825280601f01601f191660200182016040528015613048576020820181803683370190505b509050600360fc1b8160008151811061306357613063613941565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061309257613092613941565b60200101906001600160f81b031916908160001a90535060006130b6846002613ee8565b6130c1906001613888565b90505b6001811115613139576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106130f5576130f5613941565b1a60f81b82828151811061310b5761310b613941565b60200101906001600160f81b031916908160001a90535060049490941c9361313281613fc7565b90506130c4565b5083156119a55760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016104c9565b6001600160a01b038116811461319d57600080fd5b50565b600080604083850312156131b357600080fd5b8235915060208301356131c581613188565b809150509250929050565b6000602082840312156131e257600080fd5b81356001600160e01b0319811681146119a557600080fd5b60008083601f84011261320c57600080fd5b5081356001600160401b0381111561322357600080fd5b6020830191508360208260051b850101111561323e57600080fd5b9250929050565b60008083601f84011261325757600080fd5b5081356001600160401b0381111561326e57600080fd5b60208301915083602082850101111561323e57600080fd5b60008060008060008060006080888a0312156132a157600080fd5b87356001600160401b03808211156132b857600080fd5b6132c48b838c016131fa565b909950975060208a01359150808211156132dd57600080fd5b6132e98b838c016131fa565b909750955060408a013591506132fe82613188565b9093506060890135908082111561331457600080fd5b506133218a828b01613245565b989b979a50959850939692959293505050565b60006020828403121561334657600080fd5b5035919050565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b03811182821017156133855761338561334d565b60405290565b60405161010081016001600160401b03811182821017156133855761338561334d565b604051601f8201601f191681016001600160401b03811182821017156133d6576133d661334d565b604052919050565b60006001600160401b038211156133f7576133f761334d565b50601f01601f191660200190565b60008060006060848603121561341a57600080fd5b83356001600160401b0381111561343057600080fd5b8401601f8101861361344157600080fd5b803561345461344f826133de565b6133ae565b81815287602083850101111561346957600080fd5b81602084016020830137600060208383010152809550505050602084013561349081613188565b929592945050506040919091013590565b600080604083850312156134b457600080fd5b82356134bf81613188565b915060208301356131c581613188565b600080602083850312156134e257600080fd5b82356001600160401b038111156134f857600080fd5b613504858286016131fa565b90969095509350505050565b6001600160401b038116811461319d57600080fd5b60008060006060848603121561353a57600080fd5b833561354581613188565b9250602084013561355581613510565b9150604084013561356581613510565b809150509250925092565b60006020828403121561358257600080fd5b81356119a581613188565b600080604083850312156135a057600080fd5b50508035926020909101359150565b600080604083850312156135c257600080fd5b82356135cd81613188565b946020939093013593505050565b600080600080604085870312156135f157600080fd5b84356001600160401b038082111561360857600080fd5b613614888389016131fa565b9096509450602087013591508082111561362d57600080fd5b5061363a878288016131fa565b95989497509550505050565b60008060006040848603121561365b57600080fd5b83356001600160401b038082111561367257600080fd5b61367e87838801613245565b9095509350602086013591508082111561369757600080fd5b5084016040818703121561356557600080fd5b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b80518252602081015160208301526040810151604083015260608101516060830152608081015160018060a01b03808216608085015260a083015191506001600160401b0380831660a08601528160c08501511660c08601528060e08501511660e08601525050505050565b60005b83811015613768578181015183820152602001613750565b83811115613777576000848401525b50505050565b6000815180845261379581602086016020860161374d565b601f01601f19169290920160200192915050565b60006101806137b883896136e1565b6001600160a01b0387811661010085015261012084018290526137dd8285018861377d565b92508561014085015280851661016085015250509695505050505050565b6000825161380d81846020870161374d565b9190910192915050565b60006060828403121561382957600080fd5b604051606081018181106001600160401b038211171561384b5761384b61334d565b80604052508251815260208301516020820152604083015160408201528091505092915050565b634e487b7160e01b600052601160045260246000fd5b6000821982111561389b5761389b613872565b500190565b6000828210156138b2576138b2613872565b500390565b6001600160a01b0394909416845260208401929092526040830152606082015260800190565b6020808252601d908201527f53656e646572206d75737420626520746865206d61696e7461696e6572000000604082015260600190565b805161391f81613188565b919050565b60006020828403121561393657600080fd5b81516119a581613188565b634e487b7160e01b600052603260045260246000fd5b805161391f81613510565b60006040828403121561397457600080fd5b61397c613363565b9050815161398981613188565b815260208201516001600160401b038111156139a457600080fd5b8201601f810184136139b557600080fd5b80516139c361344f826133de565b8181528560208385010111156139d857600080fd5b6139e982602083016020860161374d565b602084015250909392505050565b600080828403610120811215613a0c57600080fd5b61010080821215613a1c57600080fd5b613a2461338b565b915084518252602085015160208301526040850151604083015260608501516060830152613a5460808601613914565b6080830152613a6560a08601613957565b60a0830152613a7660c08601613914565b60c0830152613a8760e08601613957565b60e08301528401519092506001600160401b03811115613aa657600080fd5b613ab285828601613962565b9150509250929050565b600060208284031215613ace57600080fd5b5051919050565b600060ff821660ff811415613aec57613aec613872565b60010192915050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6001600160a01b0387811682528681166020830152851660408201526060810184905260a060808201819052600090613b5a9083018486613af5565b98975050505050505050565b600080835481600182811c915080831680613b8257607f831692505b6020808410821415613ba257634e487b7160e01b86526022600452602486fd5b818015613bb65760018114613bc757613bf4565b60ff19861689528489019650613bf4565b60008a81526020902060005b86811015613bec5781548b820152908501908301613bd3565b505084890196505b509498975050505050505050565b606081526000613c15606083018661377d565b6001600160a01b0394851660208401529290931660409091015292915050565b6000610160613c4483886136e1565b80610100840152613c578184018761377d565b61012084019590955250506001600160a01b03919091166101409091015292915050565b6000808284036060811215613c8f57600080fd5b6040811215613c9d57600080fd5b50613ca6613363565b8351613cb181613188565b81526020840151613cc181613188565b602082015260408401519092506001600160401b03811115613aa657600080fd5b60808152600060018060a01b038086511660808401526020860151604060a0850152613d1160c085018261377d565b925050808551166020840152806020860151166040840152808416606084015250949350505050565b6001600160a01b03841681526000610140613d5860208401866136e1565b80610120840152612cb58184018561377d565b6001600160a01b038681168252858116602083015284811660408301528316606082015260a060808201819052600090613da79083018461377d565b979650505050505050565b60008060408385031215613dc557600080fd5b505080516020909101519092909150565b8481526001600160a01b03848116602083015283166040820152608060608201819052600090612cb59083018461377d565b6000610140613e1783876136e1565b80610100840152613e2a8184018661377d565b91505082610120830152949350505050565b604081526000613e50604083018587613af5565b82810360208401528335613e6381613188565b6001600160a01b03168152602084013536859003601e19018112613e8657600080fd5b840180356001600160401b03811115613e9e57600080fd5b803603861315613ead57600080fd5b60406020840152613b5a604084018260208501613af5565b600060208284031215613ed757600080fd5b815160ff811681146119a557600080fd5b6000816000190483118215151615613f0257613f02613872565b500290565b600082613f2457634e487b7160e01b600052601260045260246000fd5b500490565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351613f6181601785016020880161374d565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351613f9281602884016020880161374d565b01602801949350505050565b6020815260006119a5602083018461377d565b634e487b7160e01b600052603160045260246000fd5b600081613fd657613fd6613872565b50600019019056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564126303c860ea810f85e857ad8768056e2eebc24b7796655ff3107e4af18e3f1e77697468647261772828627974657333322c75696e743235362c75696e743235362c75696e743235362c616464726573732c75696e7436342c616464726573732c75696e743634292c616464726573732c62797465732c75696e743235362c61646472657373296465706f7369742828627974657333322c75696e743235362c75696e743235362c75696e743235362c616464726573732c75696e7436342c616464726573732c75696e743634292c62797465732c75696e7432353629a264697066735822122010722fde9ac9aa3a36d3a34f8e4eedb756ee05f4c3ee6cb32d8b2692ed1a3c4764736f6c6343000809003373776170546f4a525428616464726573732c616464726573732c616464726573732c75696e743235362c627974657329746f74616c5472616e7366657228616464726573732c616464726573732c616464726573732c616464726573732c627974657329126303c860ea810f85e857ad8768056e2eebc24b7796655ff3107e4af18e3f1e00000000000000000000000080d629cf2d775cb9b97c4a95fe2269e0e8459d3a000000000000000000000000b5c54f57d7f3ec6367db4fb5a08a1174797e221c000000000000000000000000d874e1b9ace88bfaf2d5b9859164ac1001b40303

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061018d5760003560e01c806391d14854116100de578063ca15c87311610097578063db456b6211610071578063db456b6214610431578063de3627c714610444578063eead906a14610457578063f87422541461045f57600080fd5b8063ca15c873146103f8578063d547741f1461040b578063d612ea331461041e57600080fd5b806391d148541461037457806399dce61b14610387578063a217fddf146103b7578063a2be10bd146103bf578063aa566a62146103d2578063b6b55f25146103e557600080fd5b8063366694131161014b5780636c924f2f116101255780636c924f2f146102db57806374e44a97146102ee578063772b7e97146103215780639010d07c1461034957600080fd5b8063366694131461028057806338f6c371146102b5578063658c9a09146102c857600080fd5b8062f714ce1461019257806301ffc9a7146101f0578063220ea31d14610213578063248a9ca3146102285780632f2ff15d1461025a57806336568abe1461026d575b600080fd5b6101a56101a03660046131a0565b610474565b6040516101e79190600060a082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015292915050565b60405180910390f35b6102036101fe3660046131d0565b6106ac565b60405190151581526020016101e7565b610226610221366004613286565b6106d7565b005b61024c610236366004613334565b6000908152600160208190526040909120015490565b6040519081526020016101e7565b6102266102683660046131a0565b610d23565b61022661027b3660046131a0565b610d4a565b61029361028e366004613405565b610d6c565b60408051825181526020808401519082015291810151908201526060016101e7565b6102266102c33660046134a1565b61115e565b6102266102d63660046134cf565b611235565b6102266102e9366004613525565b6114e1565b6103016102fc366004613570565b6115c9565b6040805194855260208501939093529183015260608201526080016101e7565b61033461032f3660046134a1565b61172b565b604080519283526020830191909152016101e7565b61035c61035736600461358d565b61198d565b6040516001600160a01b0390911681526020016101e7565b6102036103823660046131a0565b6119ac565b61039a6103953660046135af565b6119d7565b604080519283526001600160a01b039091166020830152016101e7565b61024c600081565b6102266103cd366004613570565b611b13565b6102266103e03660046135db565b611be2565b6101a56103f3366004613334565b611e0a565b61024c610406366004613334565b611fdd565b6102266104193660046131a0565b611ff4565b61039a61042c3660046135af565b611ffe565b61022661043f366004613570565b6120d7565b610226610452366004613646565b61216f565b6101a5612208565b61024c60008051602061400683398151915281565b6104a66040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b600260005414156104d25760405162461bcd60e51b81526004016104c9906136aa565b60405180910390fd5b6002600090815580806104e3612412565b925092509250600061057a6040518060a001604052806067815260200161402660679139853386602001518b8b6040516024016105249594939291906137a9565b60408051601f19818403018152908290529161053f916137fb565b6040519081900390206020820180516001600160e01b03166001600160e01b031990921691909117905284516001600160a01b0316906124f5565b90506000818060200190518101906105929190613817565b905060006105ad82600001518760e001518860a0015161251a565b9050836001600160a01b031663120b8e5033846020015184600001518a602001516105d89190613888565b6105e291906138a0565b84602001518a604001516105f69190613888565b85604001518b6060015161060a9190613888565b6040518563ffffffff1660e01b815260040161062994939291906138b7565b600060405180830381600087803b15801561064357600080fd5b505af1158015610657573d6000803e3d6000fd5b5050506020808401516040808b01919091528085015160608b015283518a5290830151908301516106889250613888565b60208089019190915295909501516080870152505060016000555091949350505050565b60006001600160e01b03198216635a05180f60e01b14806106d157506106d18261259c565b92915050565b6106ef600080516020614006833981519152336119ac565b61070b5760405162461bcd60e51b81526004016104c9906138dd565b8584146107495760405162461bcd60e51b815260206004820152600c60248201526b125b9d985b1a590818d85b1b60a21b60448201526064016104c9565b60006107536125d1565b6040516302abf57960e61b815275213abcb130b1b5a83937b3b930b6a932b1b2b4bb32b960511b600482015290915060009081906001600160a01b037f00000000000000000000000080d629cf2d775cb9b97c4a95fe2269e0e8459d3a169063aafd5e409060240160206040518083038186803b1580156107d357600080fd5b505afa1580156107e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061080b9190613924565b905060005b60ff81168a1115610b245760008b8b8360ff1681811061083257610832613941565b90506020020160208101906108479190613570565b905060008a8a8460ff1681811061086057610860613941565b6040516313d21cdf60e01b81526001600160a01b038681166004830152602090920293909301359350600092839250908916906313d21cdf9060240160006040518083038186803b1580156108b457600080fd5b505afa1580156108c8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526108f091908101906139f7565b915091508a6001600160a01b031682608001516001600160a01b03161461094f5760405162461bcd60e51b8152602060048201526013602482015272086ded8d8c2e8cae4c2d840dad2e6dac2e8c6d606b1b60448201526064016104c9565b600061095b8585611ffe565b5060405163152a13cd60e31b8152600481018290529091506001600160a01b0386169063a9509e6890602401602060405180830381600087803b1580156109a157600080fd5b505af11580156109b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109d99190613abc565b90506000610a166040518060a0016040528060678152602001614026606791398588866020015186306040516024016105249594939291906137a9565b9050600081806020019051810190610a2e9190613817565b905080604001518a610a409190613888565b99506000610a5b82600001518760e001518860a0015161251a565b90508b6001600160a01b031663120b8e508983600001518960200151610a819190613888565b856020015185602001518b60400151610a9a9190613888565b610aa491906138a0565b85604001518b60600151610ab89190613888565b6040518563ffffffff1660e01b8152600401610ad794939291906138b7565b600060405180830381600087803b158015610af157600080fd5b505af1158015610b05573d6000803e3d6000fd5b5050505050505050505050508080610b1c90613ad5565b915050610810565b506040516302abf57960e61b81526a2530b93b34b9aa37b5b2b760a91b60048201526000907f00000000000000000000000080d629cf2d775cb9b97c4a95fe2269e0e8459d3a6001600160a01b03169063aafd5e409060240160206040518083038186803b158015610b9557600080fd5b505afa158015610ba9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bcd9190613924565b90506000610cbe6003848a85888c8c604051602401610bf196959493929190613b1e565b60408051601f198184030181529082905291610c0c91613b66565b60408051918290039091206020830180516001600160e01b03166001600160e01b0319909216919091179052516326a966c160e11b81526001600160a01b038b81166004830152881690634d52cd829060240160206040518083038186803b158015610c7757600080fd5b505afa158015610c8b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610caf9190613924565b6001600160a01b0316906124f5565b9050837f15bc7538235a26bbf90ff27a286d54cc1f91d523cbf888e6a739fd36621ec50d82806020019051810190610cf69190613abc565b604080519182526001600160a01b03871660208301520160405180910390a2505050505050505050505050565b610d2d8282612688565b6000828152600260205260409020610d4590826123fd565b505050565b610d5482826126af565b6000828152600260205260409020610d459082612729565b610d9060405180606001604052806000815260200160008152602001600081525090565b60026000541415610db35760405162461bcd60e51b81526004016104c9906136aa565b600260009081558080610dc4612412565b925092509250600083602001519050610df760405180606001604052806000815260200160008152602001600081525090565b6000610e886040518060a001604052806067815260200161402660679139873388602001518c30604051602401610e329594939291906137a9565b60408051601f198184030181529082905291610e4d916137fb565b6040519081900390206020820180516001600160e01b03166001600160e01b031990921691909117905286516001600160a01b0316906124f5565b905080806020019051810190610e9e9190613817565b9150506000610eba82600001518760e001518860a0015161251a565b9050600081602001518760400151610ed29190613888565b9050600082604001518860600151610eea9190613888565b604051630120b8e560e41b81529091506001600160a01b0387169063120b8e5090610f20903390600090819081906004016138b7565b600060405180830381600087803b158015610f3a57600080fd5b505af1158015610f4e573d6000803e3d6000fd5b50505050610f6d60408051808201909152600081526060602082015290565b866001600160a01b031663ee3b32698e338f6040518463ffffffff1660e01b8152600401610f9d93929190613c02565b600060405180830381600087803b158015610fb757600080fd5b505af1158015610fcb573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610ff391908101906139f7565b809250819a505050600061108e60405180608001604052806056815260200161408d605691398b84602001518960400151336040516024016110389493929190613c35565b60408051601f198184030181529082905291611053916137fb565b6040519081900390206020820180516001600160e01b03166001600160e01b031990921691909117905283516001600160a01b0316906124f5565b90506000818060200190518101906110a69190613817565b90506000848683602001516110bb91906138a0565b6110c591906138a0565b604051630120b8e560e41b81529091506001600160a01b038b169063120b8e50906110fa90339085908b908b906004016138b7565b600060405180830381600087803b15801561111457600080fd5b505af1158015611128573d6000803e3d6000fd5b5050604080516060810182529b8c52985160208c01525050958801959095525050600160005550929a9950505050505050505050565b611176600080516020614006833981519152336119ac565b6111925760405162461bcd60e51b81526004016104c9906138dd565b600260005414156111b55760405162461bcd60e51b81526004016104c9906136aa565b600260009081556111c46125d1565b6040516338f6c37160e01b81526001600160a01b0385811660048301528481166024830152919250908216906338f6c37190604401600060405180830381600087803b15801561121357600080fd5b505af1158015611227573d6000803e3d6000fd5b505060016000555050505050565b61124d600080516020614006833981519152336119ac565b6112695760405162461bcd60e51b81526004016104c9906138dd565b6002600054141561128c5760405162461bcd60e51b81526004016104c9906136aa565b6002600090815561129b6125d1565b60408051808201909152600080825260208201529091506040805180820190915260008152606060208201526040516302abf57960e61b8152752632b73234b733a932bbb0b93239a932b1b2b4bb32b960511b60048201526000907f00000000000000000000000080d629cf2d775cb9b97c4a95fe2269e0e8459d3a6001600160a01b03169063aafd5e409060240160206040518083038186803b15801561134257600080fd5b505afa158015611356573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061137a9190613924565b905060005b60ff811686111561122757846001600160a01b0316632155162388888460ff168181106113ae576113ae613941565b90506020020160208101906113c39190613570565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260240160006040518083038186803b15801561140257600080fd5b505afa158015611416573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261143e9190810190613c7b565b9094509250868660ff831681811061145857611458613941565b905060200201602081019061146d9190613570565b6001600160a01b0316634f9b84778486856040518463ffffffff1660e01b815260040161149c93929190613ce2565b600060405180830381600087803b1580156114b657600080fd5b505af11580156114ca573d6000803e3d6000fd5b5050505080806114d990613ad5565b91505061137f565b6114f9600080516020614006833981519152336119ac565b6115155760405162461bcd60e51b81526004016104c9906138dd565b600260005414156115385760405162461bcd60e51b81526004016104c9906136aa565b600260009081556115476125d1565b604051636c924f2f60e01b81526001600160a01b0386811660048301526001600160401b0380871660248401528516604483015291925090821690636c924f2f906064015b600060405180830381600087803b1580156115a657600080fd5b505af11580156115ba573d6000803e3d6000fd5b50506001600055505050505050565b60008060008060006115d96125d1565b6040516313d21cdf60e01b81526001600160a01b0388811660048301529192506000918291908416906313d21cdf9060240160006040518083038186803b15801561162357600080fd5b505afa158015611637573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261165f91908101906139f7565b91509150600081600001516001600160a01b03166317e386468a8585602001516040518463ffffffff1660e01b815260040161169d93929190613d3a565b60206040518083038186803b1580156116b557600080fd5b505afa1580156116c9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116ed9190613abc565b90506000611704828560e001518660a0015161251a565b805160408201516020928301519690920151909c919b509499509397509295505050505050565b6000806117577f00000000000000000000000080d629cf2d775cb9b97c4a95fe2269e0e8459d3a61273e565b6002600054141561177a5760405162461bcd60e51b81526004016104c9906136aa565b600260009081556117896125d1565b604051632155162360e01b81526001600160a01b03878116600483015291925060009182919084169063215516239060240160006040518083038186803b1580156117d357600080fd5b505afa1580156117e7573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261180f9190810190613c7b565b91509150600061185a6004898986600001518760200151876020015160405160240161183f959493929190613d6b565b60408051601f19818403018152908290529161105391613b66565b9050600080828060200190518101906118739190613db2565b604051637d9d518b60e01b81526001600160a01b038d8116600483015292945090925090871690637d9d518b9060240160206040518083038186803b1580156118bb57600080fd5b505afa1580156118cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118f39190613abc565b975081611900828a613888565b61190a91906138a0565b6040516340ec3c6160e11b81526001600160a01b038c811660048301528b8116602483015260448201839052919850908716906381d878c290606401600060405180830381600087803b15801561196057600080fd5b505af1158015611974573d6000803e3d6000fd5b5050505050505050505060016000819055509250929050565b60008281526002602052604081206119a590836128ac565b9392505050565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b60008060006119e46125d1565b604051632155162360e01b81526001600160a01b03878116600483015291925060009182919084169063215516239060240160006040518083038186803b158015611a2e57600080fd5b505afa158015611a42573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611a6a9190810190613c7b565b9150915080600001516001600160a01b0316633ca5578c878460000151856020015185602001516040518563ffffffff1660e01b8152600401611ab09493929190613dd6565b60206040518083038186803b158015611ac857600080fd5b505afa158015611adc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b009190613abc565b9450816020015193505050509250929050565b611b2b600080516020614006833981519152336119ac565b611b475760405162461bcd60e51b81526004016104c9906138dd565b60026000541415611b6a5760405162461bcd60e51b81526004016104c9906136aa565b60026000908155611b796125d1565b60405163a2be10bd60e01b81526001600160a01b0384811660048301529192509082169063a2be10bd906024015b600060405180830381600087803b158015611bc157600080fd5b505af1158015611bd5573d6000803e3d6000fd5b5050600160005550505050565b611bfa600080516020614006833981519152336119ac565b611c165760405162461bcd60e51b81526004016104c9906138dd565b828114611c545760405162461bcd60e51b815260206004820152600c60248201526b125b9d985b1a590818d85b1b60a21b60448201526064016104c9565b6040516302abf57960e61b81527121b7b6b6b4b9b9b4b7b72932b1b2b4bb32b960711b60048201526000907f00000000000000000000000080d629cf2d775cb9b97c4a95fe2269e0e8459d3a6001600160a01b03169063aafd5e409060240160206040518083038186803b158015611ccb57600080fd5b505afa158015611cdf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d039190613924565b90506000805b60ff8116861115611dc357600085858360ff16818110611d2b57611d2b613941565b905060200201351115611db157611d8787878360ff16818110611d5057611d50613941565b9050602002016020810190611d659190613570565b86868460ff16818110611d7a57611d7a613941565b90506020020135856128b8565b84848260ff16818110611d9c57611d9c613941565b9050602002013582611dae9190613888565b91505b80611dbb81613ad5565b915050611d09565b506040516001600160a01b038316815281907f47592ec9d9991a137422beab7c67c9dde0899a7b5cc3c7df0812979abf5be8ac9060200160405180910390a2505050505050565b611e3c6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b60026000541415611e5f5760405162461bcd60e51b81526004016104c9906136aa565b600260009081558080611e70612412565b9250925092506000611eac60405180608001604052806056815260200161408d6056913960208501516040516105249188918b90602401613e08565b9050600081806020019051810190611ec49190613817565b90506000611edf82600001518760e001518860a0015161251a565b9050836001600160a01b031663120b8e5033836000015185602001518a60200151611f0a9190613888565b611f149190613888565b84602001518a60400151611f289190613888565b85604001518b60600151611f3c9190613888565b6040518563ffffffff1660e01b8152600401611f5b94939291906138b7565b600060405180830381600087803b158015611f7557600080fd5b505af1158015611f89573d6000803e3d6000fd5b5050506020808401516040808b01919091528085015160608b015283518a529083015190830151611fba9250613888565b602080890191909152959095015160808701525050600160005550919392505050565b60008181526002602052604081206106d190612b00565b610d548282612b0a565b600080600061200b6125d1565b604051632155162360e01b81526001600160a01b03878116600483015291925060009182919084169063215516239060240160006040518083038186803b15801561205557600080fd5b505afa158015612069573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526120919190810190613c7b565b9150915080600001516001600160a01b031663156b8523878460000151856020015185602001516040518563ffffffff1660e01b8152600401611ab09493929190613dd6565b6120ef600080516020614006833981519152336119ac565b61210b5760405162461bcd60e51b81526004016104c9906138dd565b6002600054141561212e5760405162461bcd60e51b81526004016104c9906136aa565b6002600090815561213d6125d1565b604051636da2b5b160e11b81526001600160a01b0384811660048301529192509082169063db456b6290602401611ba7565b612187600080516020614006833981519152336119ac565b6121a35760405162461bcd60e51b81526004016104c9906138dd565b600260005414156121c65760405162461bcd60e51b81526004016104c9906136aa565b600260009081556121d56125d1565b60405163de3627c760e01b81529091506001600160a01b0382169063de3627c79061158c90879087908790600401613e3c565b61223a6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b6002600054141561225d5760405162461bcd60e51b81526004016104c9906136aa565b60026000908155808061226e612412565b925092509250600082600001516001600160a01b031663b108ba2c338686602001516040518463ffffffff1660e01b81526004016122ae93929190613d3a565b602060405180830381600087803b1580156122c857600080fd5b505af11580156122dc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123009190613abc565b90506000612317828660e001518760a0015161251a565b9050826001600160a01b031663120b8e50338360000151886020015161233d9190613888565b846020015189604001516123519190613888565b85604001518a606001516123659190613888565b6040518563ffffffff1660e01b815260040161238494939291906138b7565b600060405180830381600087803b15801561239e57600080fd5b505af11580156123b2573d6000803e3d6000fd5b5050825188525050604081015160208201516123ce9190613888565b60208088019190915294909401516080860152505060016000555090919050565b6123f98282612b31565b5050565b60006119a5836001600160a01b038416612b9c565b60408051610100810182526000808252602080830182905282840182905260608084018390526080840183905260a0840183905260c0840183905260e08401839052845180860190955291845283015290600061246d6125d1565b6040516313d21cdf60e01b81523360048201529091506001600160a01b038216906313d21cdf9060240160006040518083038186803b1580156124af57600080fd5b505afa1580156124c3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526124eb91908101906139f7565b9094909350909150565b60606119a58383604051806060016040528060278152602001613fdf60279139612beb565b61253e60405180606001604052806000815260200160008152602001600081525090565b83612548576119a5565b600061255d856001600160401b038616612cbf565b9050612572816001600160401b038516612cbf565b6020830181905261258390826138a0565b604083015261259281866138a0565b8252509392505050565b60006001600160e01b03198216637965db0b60e01b14806106d157506301ffc9a760e01b6001600160e01b03198316146106d1565b6040516302abf57960e61b8152742632b73234b733a9ba37b930b3b2a6b0b730b3b2b960591b60048201526000907f00000000000000000000000080d629cf2d775cb9b97c4a95fe2269e0e8459d3a6001600160a01b03169063aafd5e409060240160206040518083038186803b15801561264b57600080fd5b505afa15801561265f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126839190613924565b905090565b600082815260016020819052604090912001546126a58133612cde565b610d458383612b31565b6001600160a01b038116331461271f5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084016104c9565b6123f98282612d42565b60006119a5836001600160a01b038416612da9565b6040516302abf57960e61b815270466163746f727956657273696f6e696e6760781b60048201526000906001600160a01b0383169063aafd5e409060240160206040518083038186803b15801561279457600080fd5b505afa1580156127a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127cc9190613924565b604051636d2721a360e11b81526a506f6f6c466163746f727960a81b60048201529091506000906001600160a01b0383169063da4e43469060240160206040518083038186803b15801561281f57600080fd5b505afa158015612833573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128579190613ec5565b905061287282826a506f6f6c466163746f727960a81b612e9c565b610d455760405162461bcd60e51b815260206004820152600b60248201526a139bdd08185b1b1bddd95960aa1b60448201526064016104c9565b60006119a58383612f8a565b60006128c26125d1565b6040516313d21cdf60e01b81526001600160a01b0386811660048301529192506000918291908416906313d21cdf9060240160006040518083038186803b15801561290c57600080fd5b505afa158015612920573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261294891908101906139f7565b9150915060006129588787611ffe565b5060405163152a13cd60e31b8152600481018290529091506001600160a01b0388169063a9509e6890602401602060405180830381600087803b15801561299e57600080fd5b505af11580156129b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129d69190613abc565b90506000612a136040518060a001604052806067815260200161402660679139858a8660200151868b6040516024016105249594939291906137a9565b9050600081806020019051810190612a2b9190613817565b90506000612a4682600001518760e001518860a0015161251a565b9050866001600160a01b031663120b8e508b83600001518960200151612a6c9190613888565b84602001518a60400151612a809190613888565b866020015186604001518c60600151612a999190613888565b612aa391906138a0565b6040518563ffffffff1660e01b8152600401612ac294939291906138b7565b600060405180830381600087803b158015612adc57600080fd5b505af1158015612af0573d6000803e3d6000fd5b5050505050505050505050505050565b60006106d1825490565b60008281526001602081905260409091200154612b278133612cde565b610d458383612d42565b612b3b82826119ac565b6123f95760008281526001602081815260408084206001600160a01b0386168086529252808420805460ff19169093179092559051339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b6000818152600183016020526040812054612be3575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556106d1565b5060006106d1565b6060833b612c4a5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b60648201526084016104c9565b600080856001600160a01b031685604051612c6591906137fb565b600060405180830381855af49150503d8060008114612ca0576040519150601f19603f3d011682016040523d82523d6000602084013e612ca5565b606091505b5091509150612cb5828286612fb4565b9695505050505050565b6000670de0b6b3a7640000612cd48385613ee8565b6119a59190613f07565b612ce882826119ac565b6123f957612d00816001600160a01b03166014612fed565b612d0b836020612fed565b604051602001612d1c929190613f29565b60408051601f198184030181529082905262461bcd60e51b82526104c991600401613f9e565b612d4c82826119ac565b156123f95760008281526001602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60008181526001830160205260408120548015612e92576000612dcd6001836138a0565b8554909150600090612de1906001906138a0565b9050818114612e46576000866000018281548110612e0157612e01613941565b9060005260206000200154905080876000018481548110612e2457612e24613941565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080612e5757612e57613fb1565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506106d1565b60009150506106d1565b60008060005b8460ff168260ff161015612f8157604051636839980160e11b81526004810185905260ff821660248201526001600160a01b0387169063d07330029060440160206040518083038186803b158015612ef957600080fd5b505afa925050508015612f29575060408051601f3d908101601f19168201909252612f2691810190613924565b60015b612f3257612f6f565b336001600160a01b0382161415612f4d576001935050612f81565b82612f5781613ad5565b9350508560ff168360ff161415612f6d57600093505b505b80612f7981613ad5565b915050612ea2565b50509392505050565b6000826000018281548110612fa157612fa1613941565b9060005260206000200154905092915050565b60608315612fc35750816119a5565b825115612fd35782518084602001fd5b8160405162461bcd60e51b81526004016104c99190613f9e565b60606000612ffc836002613ee8565b613007906002613888565b6001600160401b0381111561301e5761301e61334d565b6040519080825280601f01601f191660200182016040528015613048576020820181803683370190505b509050600360fc1b8160008151811061306357613063613941565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061309257613092613941565b60200101906001600160f81b031916908160001a90535060006130b6846002613ee8565b6130c1906001613888565b90505b6001811115613139576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106130f5576130f5613941565b1a60f81b82828151811061310b5761310b613941565b60200101906001600160f81b031916908160001a90535060049490941c9361313281613fc7565b90506130c4565b5083156119a55760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016104c9565b6001600160a01b038116811461319d57600080fd5b50565b600080604083850312156131b357600080fd5b8235915060208301356131c581613188565b809150509250929050565b6000602082840312156131e257600080fd5b81356001600160e01b0319811681146119a557600080fd5b60008083601f84011261320c57600080fd5b5081356001600160401b0381111561322357600080fd5b6020830191508360208260051b850101111561323e57600080fd5b9250929050565b60008083601f84011261325757600080fd5b5081356001600160401b0381111561326e57600080fd5b60208301915083602082850101111561323e57600080fd5b60008060008060008060006080888a0312156132a157600080fd5b87356001600160401b03808211156132b857600080fd5b6132c48b838c016131fa565b909950975060208a01359150808211156132dd57600080fd5b6132e98b838c016131fa565b909750955060408a013591506132fe82613188565b9093506060890135908082111561331457600080fd5b506133218a828b01613245565b989b979a50959850939692959293505050565b60006020828403121561334657600080fd5b5035919050565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b03811182821017156133855761338561334d565b60405290565b60405161010081016001600160401b03811182821017156133855761338561334d565b604051601f8201601f191681016001600160401b03811182821017156133d6576133d661334d565b604052919050565b60006001600160401b038211156133f7576133f761334d565b50601f01601f191660200190565b60008060006060848603121561341a57600080fd5b83356001600160401b0381111561343057600080fd5b8401601f8101861361344157600080fd5b803561345461344f826133de565b6133ae565b81815287602083850101111561346957600080fd5b81602084016020830137600060208383010152809550505050602084013561349081613188565b929592945050506040919091013590565b600080604083850312156134b457600080fd5b82356134bf81613188565b915060208301356131c581613188565b600080602083850312156134e257600080fd5b82356001600160401b038111156134f857600080fd5b613504858286016131fa565b90969095509350505050565b6001600160401b038116811461319d57600080fd5b60008060006060848603121561353a57600080fd5b833561354581613188565b9250602084013561355581613510565b9150604084013561356581613510565b809150509250925092565b60006020828403121561358257600080fd5b81356119a581613188565b600080604083850312156135a057600080fd5b50508035926020909101359150565b600080604083850312156135c257600080fd5b82356135cd81613188565b946020939093013593505050565b600080600080604085870312156135f157600080fd5b84356001600160401b038082111561360857600080fd5b613614888389016131fa565b9096509450602087013591508082111561362d57600080fd5b5061363a878288016131fa565b95989497509550505050565b60008060006040848603121561365b57600080fd5b83356001600160401b038082111561367257600080fd5b61367e87838801613245565b9095509350602086013591508082111561369757600080fd5b5084016040818703121561356557600080fd5b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b80518252602081015160208301526040810151604083015260608101516060830152608081015160018060a01b03808216608085015260a083015191506001600160401b0380831660a08601528160c08501511660c08601528060e08501511660e08601525050505050565b60005b83811015613768578181015183820152602001613750565b83811115613777576000848401525b50505050565b6000815180845261379581602086016020860161374d565b601f01601f19169290920160200192915050565b60006101806137b883896136e1565b6001600160a01b0387811661010085015261012084018290526137dd8285018861377d565b92508561014085015280851661016085015250509695505050505050565b6000825161380d81846020870161374d565b9190910192915050565b60006060828403121561382957600080fd5b604051606081018181106001600160401b038211171561384b5761384b61334d565b80604052508251815260208301516020820152604083015160408201528091505092915050565b634e487b7160e01b600052601160045260246000fd5b6000821982111561389b5761389b613872565b500190565b6000828210156138b2576138b2613872565b500390565b6001600160a01b0394909416845260208401929092526040830152606082015260800190565b6020808252601d908201527f53656e646572206d75737420626520746865206d61696e7461696e6572000000604082015260600190565b805161391f81613188565b919050565b60006020828403121561393657600080fd5b81516119a581613188565b634e487b7160e01b600052603260045260246000fd5b805161391f81613510565b60006040828403121561397457600080fd5b61397c613363565b9050815161398981613188565b815260208201516001600160401b038111156139a457600080fd5b8201601f810184136139b557600080fd5b80516139c361344f826133de565b8181528560208385010111156139d857600080fd5b6139e982602083016020860161374d565b602084015250909392505050565b600080828403610120811215613a0c57600080fd5b61010080821215613a1c57600080fd5b613a2461338b565b915084518252602085015160208301526040850151604083015260608501516060830152613a5460808601613914565b6080830152613a6560a08601613957565b60a0830152613a7660c08601613914565b60c0830152613a8760e08601613957565b60e08301528401519092506001600160401b03811115613aa657600080fd5b613ab285828601613962565b9150509250929050565b600060208284031215613ace57600080fd5b5051919050565b600060ff821660ff811415613aec57613aec613872565b60010192915050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6001600160a01b0387811682528681166020830152851660408201526060810184905260a060808201819052600090613b5a9083018486613af5565b98975050505050505050565b600080835481600182811c915080831680613b8257607f831692505b6020808410821415613ba257634e487b7160e01b86526022600452602486fd5b818015613bb65760018114613bc757613bf4565b60ff19861689528489019650613bf4565b60008a81526020902060005b86811015613bec5781548b820152908501908301613bd3565b505084890196505b509498975050505050505050565b606081526000613c15606083018661377d565b6001600160a01b0394851660208401529290931660409091015292915050565b6000610160613c4483886136e1565b80610100840152613c578184018761377d565b61012084019590955250506001600160a01b03919091166101409091015292915050565b6000808284036060811215613c8f57600080fd5b6040811215613c9d57600080fd5b50613ca6613363565b8351613cb181613188565b81526020840151613cc181613188565b602082015260408401519092506001600160401b03811115613aa657600080fd5b60808152600060018060a01b038086511660808401526020860151604060a0850152613d1160c085018261377d565b925050808551166020840152806020860151166040840152808416606084015250949350505050565b6001600160a01b03841681526000610140613d5860208401866136e1565b80610120840152612cb58184018561377d565b6001600160a01b038681168252858116602083015284811660408301528316606082015260a060808201819052600090613da79083018461377d565b979650505050505050565b60008060408385031215613dc557600080fd5b505080516020909101519092909150565b8481526001600160a01b03848116602083015283166040820152608060608201819052600090612cb59083018461377d565b6000610140613e1783876136e1565b80610100840152613e2a8184018661377d565b91505082610120830152949350505050565b604081526000613e50604083018587613af5565b82810360208401528335613e6381613188565b6001600160a01b03168152602084013536859003601e19018112613e8657600080fd5b840180356001600160401b03811115613e9e57600080fd5b803603861315613ead57600080fd5b60406020840152613b5a604084018260208501613af5565b600060208284031215613ed757600080fd5b815160ff811681146119a557600080fd5b6000816000190483118215151615613f0257613f02613872565b500290565b600082613f2457634e487b7160e01b600052601260045260246000fd5b500490565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351613f6181601785016020880161374d565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351613f9281602884016020880161374d565b01602801949350505050565b6020815260006119a5602083018461377d565b634e487b7160e01b600052603160045260246000fd5b600081613fd657613fd6613872565b50600019019056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564126303c860ea810f85e857ad8768056e2eebc24b7796655ff3107e4af18e3f1e77697468647261772828627974657333322c75696e743235362c75696e743235362c75696e743235362c616464726573732c75696e7436342c616464726573732c75696e743634292c616464726573732c62797465732c75696e743235362c61646472657373296465706f7369742828627974657333322c75696e743235362c75696e743235362c75696e743235362c616464726573732c75696e7436342c616464726573732c75696e743634292c62797465732c75696e7432353629a264697066735822122010722fde9ac9aa3a36d3a34f8e4eedb756ee05f4c3ee6cb32d8b2692ed1a3c4764736f6c63430008090033

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

00000000000000000000000080d629cf2d775cb9b97c4a95fe2269e0e8459d3a000000000000000000000000b5c54f57d7f3ec6367db4fb5a08a1174797e221c000000000000000000000000d874e1b9ace88bfaf2d5b9859164ac1001b40303

-----Decoded View---------------
Arg [0] : _finder (address): 0x80d629cf2D775cB9b97c4A95Fe2269e0E8459d3A
Arg [1] : _roles (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 00000000000000000000000080d629cf2d775cb9b97c4a95fe2269e0e8459d3a
Arg [1] : 000000000000000000000000b5c54f57d7f3ec6367db4fb5a08a1174797e221c
Arg [2] : 000000000000000000000000d874e1b9ace88bfaf2d5b9859164ac1001b40303


Block Transaction Gas Used Reward
view all blocks validated

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ 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.