ETH Price: $2,081.98 (+7.19%)
Gas: 0.54 Gwei
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Transfer Ownersh...235443002025-10-10 2:18:47145 days ago1760062727IN
0x83F524b4...DBa2b0C0a
0 ETH0.000003280.10640982
Approve Token235442942025-10-10 2:17:35145 days ago1760062655IN
0x83F524b4...DBa2b0C0a
0 ETH0.000007790.11391145
Approve Token235442932025-10-10 2:17:23145 days ago1760062643IN
0x83F524b4...DBa2b0C0a
0 ETH0.000006250.10490444
Add Executor235442912025-10-10 2:16:59145 days ago1760062619IN
0x83F524b4...DBa2b0C0a
0 ETH0.000005180.1088031

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0xE5165B3E...77F3976ef
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
LoanManagerOwnable2StepWithShortcut

Compiler Version
v0.8.21+commit.d9974bed

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 26 : LoanManagerOwnable2StepWithShortcut.sol
/* SPDX-License-Identifier: UNLICENSED */
pragma solidity ^0.8.0;

import './LoanManager.sol';
import '@itb/quant-common/contracts/solidity8/utils/Ownable2StepWithShortcut.sol';

contract LoanManagerOwnable2StepWithShortcut is LoanManager, Ownable2StepWithShortcut {
    constructor(address[] memory _executors, address payable _wnative, address _supplyAddress, address _borrowAddress, uint _minHealthFactor, IPool _lendingPool, IPriceOracleGetter _priceOracle, IProtocolDataProviderV3 _protocolDataProvider, IRewardsControllerV3 _rewardsController) LoanManager(_executors, _wnative, _supplyAddress, _borrowAddress, _minHealthFactor, _lendingPool, _priceOracle, _protocolDataProvider, _rewardsController) {}
}

File 2 of 26 : DataTypesV3.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.10;

library DataTypesV3 {
  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;
  }

  struct ReserveConfigurationMap {
    //bit 0-15: LTV
    //bit 16-31: Liq. threshold
    //bit 32-47: Liq. bonus
    //bit 48-55: Decimals
    //bit 56: reserve is active
    //bit 57: reserve is frozen
    //bit 58: borrowing is enabled
    //bit 59: stable rate borrowing enabled
    //bit 60: asset is paused
    //bit 61: borrowing in isolation mode is enabled
    //bit 62-63: reserved
    //bit 64-79: reserve factor
    //bit 80-115 borrow cap in whole tokens, borrowCap == 0 => no cap
    //bit 116-151 supply cap in whole tokens, supplyCap == 0 => no cap
    //bit 152-167 liquidation protocol fee
    //bit 168-175 eMode category
    //bit 176-211 unbacked mint cap in whole tokens, unbackedMintCap == 0 => minting disabled
    //bit 212-251 debt ceiling for isolation mode with (ReserveConfiguration::DEBT_CEILING_DECIMALS) decimals
    //bit 252-255 unused

    uint256 data;
  }

  struct UserConfigurationMap {
    /**
     * @dev Bitmap of the users collaterals and borrows. It is divided in pairs of bits, one pair per asset.
     * The first bit indicates if an asset is used as collateral by the user, the second whether an
     * asset is borrowed by the user.
     */
    uint256 data;
  }

  struct EModeCategory {
    // each eMode category has a custom ltv and liquidation threshold
    uint16 ltv;
    uint16 liquidationThreshold;
    uint16 liquidationBonus;
    // each eMode category may or may not have a custom oracle to override the individual assets price oracles
    address priceSource;
    string label;
  }

  enum InterestRateMode {
    NONE,
    STABLE,
    VARIABLE
  }

  struct ReserveCache {
    uint256 currScaledVariableDebt;
    uint256 nextScaledVariableDebt;
    uint256 currPrincipalStableDebt;
    uint256 currAvgStableBorrowRate;
    uint256 currTotalStableDebt;
    uint256 nextAvgStableBorrowRate;
    uint256 nextTotalStableDebt;
    uint256 currLiquidityIndex;
    uint256 nextLiquidityIndex;
    uint256 currVariableBorrowIndex;
    uint256 nextVariableBorrowIndex;
    uint256 currLiquidityRate;
    uint256 currVariableBorrowRate;
    uint256 reserveFactor;
    ReserveConfigurationMap reserveConfiguration;
    address aTokenAddress;
    address stableDebtTokenAddress;
    address variableDebtTokenAddress;
    uint40 reserveLastUpdateTimestamp;
    uint40 stableDebtLastUpdateTimestamp;
  }

  struct ExecuteLiquidationCallParams {
    uint256 reservesCount;
    uint256 debtToCover;
    address collateralAsset;
    address debtAsset;
    address user;
    bool receiveAToken;
    address priceOracle;
    uint8 userEModeCategory;
    address priceOracleSentinel;
  }

  struct ExecuteSupplyParams {
    address asset;
    uint256 amount;
    address onBehalfOf;
    uint16 referralCode;
  }

  struct ExecuteBorrowParams {
    address asset;
    address user;
    address onBehalfOf;
    uint256 amount;
    InterestRateMode interestRateMode;
    uint16 referralCode;
    bool releaseUnderlying;
    uint256 maxStableRateBorrowSizePercent;
    uint256 reservesCount;
    address oracle;
    uint8 userEModeCategory;
    address priceOracleSentinel;
  }

  struct ExecuteRepayParams {
    address asset;
    uint256 amount;
    InterestRateMode interestRateMode;
    address onBehalfOf;
    bool useATokens;
  }

  struct ExecuteWithdrawParams {
    address asset;
    uint256 amount;
    address to;
    uint256 reservesCount;
    address oracle;
    uint8 userEModeCategory;
  }

  struct ExecuteSetUserEModeParams {
    uint256 reservesCount;
    address oracle;
    uint8 categoryId;
  }

  struct FinalizeTransferParams {
    address asset;
    address from;
    address to;
    uint256 amount;
    uint256 balanceFromBefore;
    uint256 balanceToBefore;
    uint256 reservesCount;
    address oracle;
    uint8 fromEModeCategory;
  }

  struct FlashloanParams {
    address receiverAddress;
    address[] assets;
    uint256[] amounts;
    uint256[] interestRateModes;
    address onBehalfOf;
    bytes params;
    uint16 referralCode;
    uint256 flashLoanPremiumToProtocol;
    uint256 flashLoanPremiumTotal;
    uint256 maxStableRateBorrowSizePercent;
    uint256 reservesCount;
    address addressesProvider;
    uint8 userEModeCategory;
    bool isAuthorizedFlashBorrower;
  }

  struct FlashloanSimpleParams {
    address receiverAddress;
    address asset;
    uint256 amount;
    bytes params;
    uint16 referralCode;
    uint256 flashLoanPremiumToProtocol;
    uint256 flashLoanPremiumTotal;
  }

  struct FlashLoanRepaymentParams {
    uint256 amount;
    uint256 totalPremium;
    uint256 flashLoanPremiumToProtocol;
    address asset;
    address receiverAddress;
    uint16 referralCode;
  }

  struct CalculateUserAccountDataParams {
    UserConfigurationMap userConfig;
    uint256 reservesCount;
    address user;
    address oracle;
    uint8 userEModeCategory;
  }

  struct ValidateBorrowParams {
    ReserveCache reserveCache;
    UserConfigurationMap userConfig;
    address asset;
    address userAddress;
    uint256 amount;
    InterestRateMode interestRateMode;
    uint256 maxStableLoanPercent;
    uint256 reservesCount;
    address oracle;
    uint8 userEModeCategory;
    address priceOracleSentinel;
    bool isolationModeActive;
    address isolationModeCollateralAddress;
    uint256 isolationModeDebtCeiling;
  }

  struct ValidateLiquidationCallParams {
    ReserveCache debtReserveCache;
    uint256 totalDebt;
    uint256 healthFactor;
    address priceOracleSentinel;
  }

  struct CalculateInterestRatesParams {
    uint256 unbacked;
    uint256 liquidityAdded;
    uint256 liquidityTaken;
    uint256 totalStableDebt;
    uint256 totalVariableDebt;
    uint256 averageStableBorrowRate;
    uint256 reserveFactor;
    address reserve;
    address aToken;
  }

  struct InitReserveParams {
    address asset;
    address aTokenAddress;
    address stableDebtAddress;
    address variableDebtAddress;
    address interestRateStrategyAddress;
    uint16 reservesCount;
    uint16 maxNumberReserves;
  }
}

/* SPDX-License-Identifier: UNLICENSED */
pragma solidity ^0.8.0;

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

struct EModeCategory {
  // each eMode category has a custom ltv and liquidation threshold
  uint16 ltv;
  uint16 liquidationThreshold;
  uint16 liquidationBonus;
  // each eMode category may or may not have a custom oracle to override the individual assets price oracles
  address priceSource;
  string label;
}

interface IPool {

  /**
   * @dev Emitted on withdraw()
   * @param reserve The address of the underlying asset being withdrawn
   * @param user The address initiating the withdrawal, owner of aTokens
   * @param to The address that will receive the underlying
   * @param amount The amount to be withdrawn
   */
  event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount);

  /**
   * @dev Emitted on repay()
   * @param reserve The address of the underlying asset of the reserve
   * @param user The beneficiary of the repayment, getting his debt reduced
   * @param repayer The address of the user initiating the repay(), providing the funds
   * @param amount The amount repaid
   * @param useATokens True if the repayment is done using aTokens, `false` if done with underlying asset directly
   */
  event Repay(
    address indexed reserve,
    address indexed user,
    address indexed repayer,
    uint256 amount,
    bool useATokens
  );

  /**
   * @notice Repays a borrowed `amount` on a specific reserve using the reserve aTokens, burning the
   * equivalent debt tokens
   * - E.g. User repays 100 USDC using 100 aUSDC, burning 100 variable/stable debt tokens
   * @dev  Passing uint256.max as amount will clean up any residual aToken dust balance, if the user aToken
   * balance is not enough to cover the whole debt
   * @param asset The address of the borrowed underlying asset previously borrowed
   * @param amount The amount to repay
   * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
   * @param interestRateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable
   * @return The final amount repaid
   **/
  function repayWithATokens(
    address asset,
    uint256 amount,
    uint256 interestRateMode
  ) external returns (uint256);

  /**
   * @notice Returns the data of an eMode category
   * @param id The id of the category
   * @return The configuration data of the category
   */
  function getEModeCategoryData(uint8 id) external view returns (EModeCategory memory);

  /**
   * @notice Allows a user to use the protocol in eMode
   * @param categoryId The id of the category
   */
  function setUserEMode(uint8 categoryId) external;

  /**
   * @notice Returns the eMode the user is using
   * @param user The address of the user
   * @return The eMode id
   */
  function getUserEMode(address user) external view returns (uint256);

  /**
   * @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 Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower
   * already supplied enough collateral, or he was given enough allowance by a credit delegator on the
   * corresponding debt token (StableDebtToken or VariableDebtToken)
   * - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet
   *   and 100 stable/variable debt tokens, depending on the `interestRateMode`
   * @param asset The address of the underlying asset to borrow
   * @param amount The amount to be borrowed
   * @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable
   * @param referralCode The 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
   * @param onBehalfOf The address of the user who will receive the debt. Should be the address of the borrower itself
   * calling the function if he wants to borrow against his own collateral, or the address of the credit delegator
   * if he has been given credit delegation allowance
   **/
  function borrow(
    address asset,
    uint256 amount,
    uint256 interestRateMode,
    uint16 referralCode,
    address onBehalfOf
  ) external;

  /**
   * @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned
   * - E.g. User repays 100 USDC, burning 100 variable/stable debt tokens of the `onBehalfOf` address
   * @param asset The address of the borrowed underlying asset previously borrowed
   * @param amount The amount to repay
   * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
   * @param interestRateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable
   * @param onBehalfOf The address of the user who will get his debt reduced/removed. Should be the address of the
   * user calling the function if he wants to reduce/remove his own debt, or the address of any other
   * other borrower whose debt should be removed
   * @return The final amount repaid
   **/
  function repay(
    address asset,
    uint256 amount,
    uint256 interestRateMode,
    address onBehalfOf
  ) external returns (uint256);

  /**
   * @notice Allows suppliers to enable/disable a specific supplied asset as collateral
   * @param asset The address of the underlying asset supplied
   * @param useAsCollateral True if the user wants to use the supply as collateral, false otherwise
   **/
  function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external;

  /**
   * @notice Returns the user account data across all the reserves
   * @param user The address of the user
   * @return totalCollateralBase The total collateral of the user in the base currency used by the price feed
   * @return totalDebtBase The total debt of the user in the base currency used by the price feed
   * @return availableBorrowsBase The borrowing power left of the user in the base currency used by the price feed
   * @return currentLiquidationThreshold The liquidation threshold of the user
   * @return ltv The loan to value of The user
   * @return healthFactor The current health factor of the user
   **/
  function getUserAccountData(address user)
    external
    view
    returns (
      uint256 totalCollateralBase,
      uint256 totalDebtBase,
      uint256 availableBorrowsBase,
      uint256 currentLiquidationThreshold,
      uint256 ltv,
      uint256 healthFactor
    );
  
  /**
   * @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 (DataTypesV3.ReserveData memory);

  /**
   * @dev Returns the configuration of the reserve
   * @param asset The address of the underlying asset of the reserve
   * @return The configuration of the reserve
   **/
  function getConfiguration(address asset) external view returns (uint);
}

// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.8.0;

interface IPriceOracleGetter {
    function getAssetPrice(address _asset) external view returns (uint256);
    function getAssetsPrices(address[] calldata _assets) external view returns(uint256[] memory);
    function getSourceOfAsset(address _asset) external view returns(address);
    function getFallbackOracle() external view returns(address);
}

// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.8.0;

interface IProtocolDataProviderV3 {
  function getReserveTokensAddresses(address asset) external view returns (address aTokenAddress, address stableDebtTokenAddress, address variableDebtTokenAddress);

  function getReserveConfigurationData(address asset) external view returns (uint256 decimals, uint256 ltv, uint256 liquidationThreshold, uint256 liquidationBonus, uint256 reserveFactor, bool collateralEnabled, bool borrowingEnabled, bool stableRateBorrowingEnabled, bool isActive, bool isFrozen);

  function getReserveData(address asset) external view returns (uint256 unbacked, uint256 accruedToTreasuryScaled, uint256 totalAToken, uint256 totalStableDebt, uint256 totalVariableDebt, uint256 liquidityRate, uint256 variableBorrowRate, uint256 stableBorrowRate, uint256 averageStableBorrowRate, uint256 liquidityIndex, uint256 variableBorrowIndex, uint40 lastUpdateTimestamp);
  
  function getReserveCaps(address asset) external view returns (uint256 borrowCap, uint256 supplyCap);

  function getUserReserveData(address asset, address user) external view returns (uint256 aTokenBalance, uint256 stableDebt, uint256 variableDebt, uint256 principalStableDebt, uint256 scaledVariableDebt, uint256 stableBorrowRate, uint256 reserveLiquidityRate, uint40 stableRateLastUpdate, bool usedAsCollateral);
}

// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.8.0;

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

/**
 * @title IRewardsController
 * @author Aave
 * @notice Defines the basic interface for a Rewards Controller.
 */
interface IRewardsControllerV3 is IRewardsDistributorV3 {
  /**
   * @dev Emitted when a new address is whitelisted as claimer of rewards on behalf of a user
   * @param user The address of the user
   * @param claimer The address of the claimer
   */
  event ClaimerSet(address indexed user, address indexed claimer);

  /**
   * @dev Emitted when rewards are claimed
   * @param user The address of the user rewards has been claimed on behalf of
   * @param reward The address of the token reward is claimed
   * @param to The address of the receiver of the rewards
   * @param claimer The address of the claimer
   * @param amount The amount of rewards claimed
   */
  event RewardsClaimed(
    address indexed user,
    address indexed reward,
    address indexed to,
    address claimer,
    uint256 amount
  );

  /**
   * @dev Emitted when a transfer strategy is installed for the reward distribution
   * @param reward The address of the token reward
   * @param transferStrategy The address of TransferStrategy contract
   */
  event TransferStrategyInstalled(address indexed reward, address indexed transferStrategy);

  /**
   * @dev Emitted when the reward oracle is updated
   * @param reward The address of the token reward
   * @param rewardOracle The address of oracle
   */
  event RewardOracleUpdated(address indexed reward, address indexed rewardOracle);

  /**
   * @dev Whitelists an address to claim the rewards on behalf of another address
   * @param user The address of the user
   * @param claimer The address of the claimer
   */
  function setClaimer(address user, address claimer) external;

  /**
   * @dev Get the price aggregator oracle address
   * @param reward The address of the reward
   * @return The price oracle of the reward
   */
  function getRewardOracle(address reward) external view returns (address);

  /**
   * @dev Returns the whitelisted claimer for a certain address (0x0 if not set)
   * @param user The address of the user
   * @return The claimer address
   */
  function getClaimer(address user) external view returns (address);

  /**
   * @dev Returns the Transfer Strategy implementation contract address being used for a reward address
   * @param reward The address of the reward
   * @return The address of the TransferStrategy contract
   */
  function getTransferStrategy(address reward) external view returns (address);

  /**
   * @dev Called by the corresponding asset on any update that affects the rewards distribution
   * @param user The address of the user
   * @param userBalance The user balance of the asset
   * @param totalSupply The total supply of the asset
   **/
  function handleAction(
    address user,
    uint256 userBalance,
    uint256 totalSupply
  ) external;

  /**
   * @dev Claims reward for a user to the desired address, on all the assets of the pool, accumulating the pending rewards
   * @param assets List of assets to check eligible distributions before claiming rewards
   * @param amount The amount of rewards to claim
   * @param to The address that will be receiving the rewards
   * @param reward The address of the reward token
   * @return The amount of rewards claimed
   **/
  function claimRewards(
    address[] calldata assets,
    uint256 amount,
    address to,
    address reward
  ) external returns (uint256);

  /**
   * @dev Claims reward for a user on behalf, on all the assets of the pool, accumulating the pending rewards. The
   * caller must be whitelisted via "allowClaimOnBehalf" function by the RewardsAdmin role manager
   * @param assets The list of assets to check eligible distributions before claiming rewards
   * @param amount The amount of rewards to claim
   * @param user The address to check and claim rewards
   * @param to The address that will be receiving the rewards
   * @param reward The address of the reward token
   * @return The amount of rewards claimed
   **/
  function claimRewardsOnBehalf(
    address[] calldata assets,
    uint256 amount,
    address user,
    address to,
    address reward
  ) external returns (uint256);

  /**
   * @dev Claims reward for msg.sender, 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 amount The amount of rewards to claim
   * @param reward The address of the reward token
   * @return The amount of rewards claimed
   **/
  function claimRewardsToSelf(
    address[] calldata assets,
    uint256 amount,
    address reward
  ) external returns (uint256);

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

  /**
   * @dev Claims all rewards for a user on behalf, on all the assets of the pool, accumulating the pending rewards. The caller must
   * be whitelisted via "allowClaimOnBehalf" function by the RewardsAdmin role manager
   * @param assets The list of assets to check eligible distributions before claiming rewards
   * @param user The address to check and claim 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 "rewardsList"
   **/
  function claimAllRewardsOnBehalf(
    address[] calldata assets,
    address user,
    address to
  ) external returns (address[] memory rewardsList, uint256[] memory claimedAmounts);

  /**
   * @dev Claims all reward for msg.sender, on all the assets of the pool, accumulating the pending rewards
   * @param assets The list of assets to check eligible distributions before claiming rewards
   * @return rewardsList List of addresses of the reward tokens
   * @return claimedAmounts List that contains the claimed amount per reward, following same order as "rewardsList"
   **/
  function claimAllRewardsToSelf(address[] calldata assets)
    external
    returns (address[] memory rewardsList, uint256[] memory claimedAmounts);
}

// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.8.0;

/**
 * @title IRewardsDistributor
 * @author Aave
 * @notice Defines the basic interface for a Rewards Distributor.
 */
interface IRewardsDistributorV3 {
  /**
   * @dev Emitted when the configuration of the rewards of an asset is updated.
   * @param asset The address of the incentivized asset
   * @param reward The address of the reward token
   * @param oldEmission The old emissions per second value of the reward distribution
   * @param newEmission The new emissions per second value of the reward distribution
   * @param oldDistributionEnd The old end timestamp of the reward distribution
   * @param newDistributionEnd The new end timestamp of the reward distribution
   * @param assetIndex The index of the asset distribution
   */
  event AssetConfigUpdated(
    address indexed asset,
    address indexed reward,
    uint256 oldEmission,
    uint256 newEmission,
    uint256 oldDistributionEnd,
    uint256 newDistributionEnd,
    uint256 assetIndex
  );

  /**
   * @dev Emitted when rewards of an asset are accrued on behalf of a user.
   * @param asset The address of the incentivized asset
   * @param reward The address of the reward token
   * @param user The address of the user that rewards are accrued on behalf of
   * @param assetIndex The index of the asset distribution
   * @param userIndex The index of the asset distribution on behalf of the user
   * @param rewardsAccrued The amount of rewards accrued
   */
  event Accrued(
    address indexed asset,
    address indexed reward,
    address indexed user,
    uint256 assetIndex,
    uint256 userIndex,
    uint256 rewardsAccrued
  );

  /**
   * @dev Emitted when the emission manager address is updated.
   * @param oldEmissionManager The address of the old emission manager
   * @param newEmissionManager The address of the new emission manager
   */
  event EmissionManagerUpdated(
    address indexed oldEmissionManager,
    address indexed newEmissionManager
  );

  /**
   * @dev Sets the end date for the distribution
   * @param asset The asset to incentivize
   * @param reward The reward token that incentives the asset
   * @param newDistributionEnd The end date of the incentivization, in unix time format
   **/
  function setDistributionEnd(
    address asset,
    address reward,
    uint32 newDistributionEnd
  ) external;

  /**
   * @dev Sets the emission per second of a set of reward distributions
   * @param asset The asset is being incentivized
   * @param rewards List of reward addresses are being distributed
   * @param newEmissionsPerSecond List of new reward emissions per second
   */
  function setEmissionPerSecond(
    address asset,
    address[] calldata rewards,
    uint88[] calldata newEmissionsPerSecond
  ) external;

  /**
   * @dev Gets the end date for the distribution
   * @param asset The incentivized asset
   * @param reward The reward token of the incentivized asset
   * @return The timestamp with the end of the distribution, in unix time format
   **/
  function getDistributionEnd(address asset, address reward) external view returns (uint256);

  /**
   * @dev Returns the index of a user on a reward distribution
   * @param user Address of the user
   * @param asset The incentivized asset
   * @param reward The reward token of the incentivized asset
   * @return The current user asset index, not including new distributions
   **/
  function getUserAssetIndex(
    address user,
    address asset,
    address reward
  ) external view returns (uint256);

  /**
   * @dev Returns the configuration of the distribution reward for a certain asset
   * @param asset The incentivized asset
   * @param reward The reward token of the incentivized asset
   * @return The index of the asset distribution
   * @return The emission per second of the reward distribution
   * @return The timestamp of the last update of the index
   * @return The timestamp of the distribution end
   **/
  function getRewardsData(address asset, address reward)
    external
    view
    returns (
      uint256,
      uint256,
      uint256,
      uint256
    );

  /**
   * @dev Returns the list of available reward token addresses of an incentivized asset
   * @param asset The incentivized asset
   * @return List of rewards addresses of the input asset
   **/
  function getRewardsByAsset(address asset) external view returns (address[] memory);

  /**
   * @dev Returns the list of available reward addresses
   * @return List of rewards supported in this contract
   **/
  function getRewardsList() external view returns (address[] memory);

  /**
   * @dev Returns the accrued rewards balance of a user, not including virtually accrued rewards since last distribution.
   * @param user The address of the user
   * @param reward The address of the reward token
   * @return Unclaimed rewards, not including new distributions
   **/
  function getUserAccruedRewards(address user, address reward) external view returns (uint256);

  /**
   * @dev Returns a single rewards balance of a user, including virtually accrued and unrealized claimable rewards.
   * @param assets List of incentivized assets to check eligible distributions
   * @param user The address of the user
   * @param reward The address of the reward token
   * @return The rewards amount
   **/
  function getUserRewards(
    address[] calldata assets,
    address user,
    address reward
  ) external view returns (uint256);

  /**
   * @dev Returns a list all rewards of a user, including already accrued and unrealized claimable rewards
   * @param assets List of incentivized assets to check eligible distributions
   * @param user The address of the user
   * @return The list of reward addresses
   * @return The list of unclaimed amount of rewards
   **/
  function getAllUserRewards(address[] calldata assets, address user)
    external
    view
    returns (address[] memory, uint256[] memory);

  /**
   * @dev Returns the decimals of an asset to calculate the distribution delta
   * @param asset The address to retrieve decimals
   * @return The decimals of an underlying asset
   */
  function getAssetDecimals(address asset) external view returns (uint8);

  /**
   * @dev Returns the address of the emission manager
   * @return The address of the EmissionManager
   */
  function getEmissionManager() external view returns (address);

  /**
   * @dev Updates the address of the emission manager
   * @param emissionManager The address of the new EmissionManager
   */
  function setEmissionManager(address emissionManager) external;
}

File 8 of 26 : LoanManager.sol
/* SPDX-License-Identifier: UNLICENSED */
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import '@itb/quant-common/contracts/solidity8/ITBContract.sol';
import '@itb/quant-common/contracts/solidity8/ILoanManager.sol';
import './aaveV3/IPriceOracle.sol';
import './aaveV3/IPool.sol';
import './aaveV3/IProtocolDataProviderV3.sol';
import './aaveV3/IRewardsControllerV3.sol';

/// @title LoanManager that targets Aave V2 markets.
/// @author IntoTheBlock Corp
contract LoanManager is ITBContract, ILoanManager {
    enum SnapshotUpdateAction { SUPPLY, BORROW, REPAY, WITHDRAW }
    event SnapshotUpdate(SnapshotUpdateAction indexed action, uint256 amount, address executor, address supplyToken, address borrowToken, uint256 totalSupplied, uint256 totalDebt);
    event ClaimRewards(address[] claimedTokens, uint256[] claimedAmounts);
    event UpdateLoanConfig(LoanConfig oldConfig, LoanConfig newConfig);
    event EnableCollateral(address supplyToken);
    event DisableCollateral(address supplyToken);
    event UpdateLendingPool(IPool oldPool, IPool newPool);
    event UpdatePriceOracle(IPriceOracleGetter oldOracle, IPriceOracleGetter newOracle);
    event UpdateProtocolDataProvider(IProtocolDataProviderV3 oldProvider, IProtocolDataProviderV3 newProvider);
    event UpdateRewardsController(IRewardsControllerV3 oldController, IRewardsControllerV3 newController);
    event UpdateEModeCategory(uint8 emode_category);
    event SetEMode(uint8 emode_category);

    IPriceOracleGetter priceOracle;
    IPool lendingPool;
    IProtocolDataProviderV3 protocolDataProvider;
    IRewardsControllerV3 rewardsController;
    LoanConfig public loanConfig;
    uint8 public emodeCategory;

    /// @param _executors Array of executors addresses.
    /// @param _wnative Native token address.
    /// @param _supplyAddress Supplied token address.
    /// @param _borrowAddress Borrowed token address.
    /// @param _minHealthFactor Minimum health factor. 1e18 is equivalent to 1.00.
    /// @param _lendingPool Address of the Aave V3 lending pool.
    /// @param _priceOracle Address of the Aave V3 price oracle.
    /// @param _protocolDataProvider Address of the Aave V3 protocol data provider.
    /// @param _rewardsController Address of the Aave V3 protocol rewards controller.
    constructor(
        address[] memory _executors,
        address payable _wnative,
        address _supplyAddress,
        address _borrowAddress,
        uint _minHealthFactor,
        IPool _lendingPool,
        IPriceOracleGetter _priceOracle,
        IProtocolDataProviderV3 _protocolDataProvider,
        IRewardsControllerV3 _rewardsController
    ) ITBContract (_executors, _wnative) {
        setConfig(_supplyAddress, _borrowAddress, _minHealthFactor);
        updateLendingPool(_lendingPool);
        updatePriceOracle(_priceOracle);
        updateProtocolDataProvider(_protocolDataProvider);
        updateRewardsController(_rewardsController);
    }

    /* Helpers */

    /// @notice Compares after execution health factor vs min health factor and reverts if below min health factor
    modifier healthFactorCheck() {
        _;
        require(getHealthFactor() >= loanConfig.minHealthFactor, "Dangerous Health Factor reached");
    }

    modifier hasIncentivesController() {
        require(address(rewardsController) != address(0), 'C1'); // IncentivesController not set
        _;
    }

    /// @notice Converts a given amount from base to asset using the oracle and returns the result.
    /// @param asset Address of the asset to convert to.
    /// @param baseAmount Amount in base token to be converted.
    /// @return convertedAmount amount converted.
    function _baseToAsset(address asset, uint baseAmount) internal view returns (uint convertedAmount) {
        return baseAmount * (10 ** _decimals(asset)) / priceOracle.getAssetPrice(asset);
    }

    /// @notice Fetches the number of decimals of a given asset.
    /// @param asset Address of the asset to fetch the decimals from.
    /// @return decimals decimals of the asset.
    function _decimals(address asset) internal view returns (uint decimals) {
        decimals = ERC20(asset).decimals();
    }

    /// @notice Atoken addresses for a given market.
    /// @param _token Underlying market.
    /// @return aToken aToken address.
    /// @return stableDebtToken Stable debt token address.
    /// @return variableDebtToken Variable debt token address.
    function _getAaveTokens(address _token) internal view returns (address aToken, address stableDebtToken, address variableDebtToken) {
        (aToken, stableDebtToken, variableDebtToken) = protocolDataProvider.getReserveTokensAddresses(_token);
    }

    /// @notice Checks if the supplied asset is enabled as collateral
    /// @return isCollateral true if the asset is enabled as collateral, false otherwise
    function _isCollateral() internal view returns (bool isCollateral){
        (, , , , , , , , isCollateral) = protocolDataProvider.getUserReserveData(loanConfig.supplyAddress, address(this));
    }

    function _logSnapshot(SnapshotUpdateAction _action, uint256 _amount) internal {
        emit SnapshotUpdate(_action, _amount, msg.sender, loanConfig.supplyAddress, loanConfig.borrowAddress, getSupply(), getBorrow());
    }
    
    /* Internal */

    /// @notice Fetches the current supply rate of the configured supply market (internal implementation).
    /// @return supplyRate Supply rate.
    function _getSupplyRate() internal view returns (uint256 supplyRate){
        (, , , , , supplyRate, , , , , ,) = protocolDataProvider.getReserveData(loanConfig.supplyAddress);
    }

    /// @notice Fetches the current borrow rate of the configured borrow market (internal implementation).
    /// @return borrowRate Borrow rate.
    function _getBorrowRate() internal view returns (uint256 borrowRate){
        (, , , , , , borrowRate, , , , ,) = protocolDataProvider.getReserveData(loanConfig.borrowAddress);
    }

    /// @notice Fetches total supply and debt of the configured markets (internal implementation).
    /// @return supplyMarketDeposits Total supply of the configured supply market.
    /// @return supplyMarketDebt Total debt of the configured supply market.
    /// @return borrowMarketDeposits Total supply of the configured borrow market.
    /// @return borrowMarketDebt Total debt of the configured borrow market.
    function _getMarketsData() internal view returns (uint256 supplyMarketDeposits, uint256 supplyMarketDebt, uint256 borrowMarketDeposits, uint256 borrowMarketDebt){
        (, , uint256 supplyMarketTotalSupply, uint256 supplyMarketStableDebt, uint256 supplyMarketVariableDebt, , , , , , , ) = protocolDataProvider.getReserveData(loanConfig.supplyAddress);
        (, , uint256 borrowMarketTotalSupply, uint256 borrowMarketStableDebt, uint256 borrowMarketVariableDebt, , , , , , , ) = protocolDataProvider.getReserveData(loanConfig.borrowAddress);
        supplyMarketDeposits = supplyMarketTotalSupply;
        supplyMarketDebt = supplyMarketStableDebt + supplyMarketVariableDebt;
        borrowMarketDeposits = borrowMarketTotalSupply;
        borrowMarketDebt = borrowMarketStableDebt + borrowMarketVariableDebt;
    }
    
    /// @notice Supplies the given amount of supply token to pool (internal implementation).
    /// @param _amount Amount of token to supply.
    function _supply(uint256 _amount) internal {
        lendingPool.supply(loanConfig.supplyAddress, _amount, address(this), 0);
    }
    
    /// @notice Borrows the given amount of borrow token from pool (internal implementation).
    /// @param _amount Amount of token to borrow.
    function _borrow(uint256 _amount) internal {
        lendingPool.borrow(loanConfig.borrowAddress, _amount, 2, 0, address(this));
    }

    /// @notice Repays the given amount of borrow token debt to lending pool (internal implementation).
    /// @param _amount Amount of debt to repay.
    function _repay(uint256 _amount) internal virtual {
        _checkAllowanceAndApprove(loanConfig.borrowAddress, address(lendingPool), _amount);
        lendingPool.repay(loanConfig.borrowAddress, _amount, 2, address(this));
    }

    /// @notice Withdraw supplied tokens from a given market (internal implementation).
    /// @param _amount Amount to withdraw.
    function _withdrawSupply(uint256 _amount) internal virtual {
        lendingPool.withdraw(loanConfig.supplyAddress, _amount, address(this));
    }

    /// @notice Claims pending rewards for the configured markets (internal implementation).
    /// @param tokens Array of token addresses to claim.
    /// @return claimedTokens Array of claimed tokens addresses.
    /// @return claimedAmounts Array of claimed amounts.
    function _claimRewards(address[] memory tokens) internal returns (address[] memory claimedTokens, uint256 [] memory claimedAmounts){
        (claimedTokens, claimedAmounts) = rewardsController.claimAllRewards(tokens, address(this));
    }
    
    /* Infra */

    /// @notice Only owner. Sets V3 config parameters.
    /// @param _supplyAddress Address of the token to supply to pool.
    /// @param _borrowAddress Address of the token to borrow from pool.
    /// @param _minHealthFactor Minimum acceptable health factor.
    function setConfig(address _supplyAddress, address _borrowAddress, uint _minHealthFactor) public onlyOwner {
        LoanConfig memory new_config = LoanConfig(_supplyAddress, _borrowAddress, _minHealthFactor);
        emit UpdateLoanConfig(loanConfig, new_config);
        loanConfig = new_config;
    }

    /// @notice Only owner. Sets V3 config parameters.
    /// @param _supplyAddress Address of the token to supply to pool.
    /// @param _borrowAddress Address of the token to borrow from pool.
    /// @param _minHealthFactor Minimum acceptable health factor.
    /// @param _emode_category Emode category to use
    function setConfig(address _supplyAddress, address _borrowAddress, uint _minHealthFactor, uint8 _emode_category) public onlyOwner {
        setConfig(_supplyAddress, _borrowAddress, _minHealthFactor);

        emit UpdateEModeCategory(_emode_category);
        emodeCategory = _emode_category;
    }

    /// @notice Only owner. Sets EMode category to the one set by config
    function setEMode() external onlyOwner {
        lendingPool.setUserEMode(emodeCategory);
        emit SetEMode(emodeCategory);
    }

    /// @notice Only owner. Enables supplied token to be used as collateral.
    function enableCollateral() external onlyOwner {
        lendingPool.setUserUseReserveAsCollateral(loanConfig.supplyAddress, true);
        emit EnableCollateral(loanConfig.supplyAddress);
    }

    /// @notice Only owner. Disables supplied token to be used as collateral.
    function disableCollateral() external onlyOwner healthFactorCheck {
        lendingPool.setUserUseReserveAsCollateral(loanConfig.supplyAddress, false);
        emit DisableCollateral(loanConfig.supplyAddress);
    }

    /// @notice Only owner. Update LendingPool address
    /// @param _address New address
    function updateLendingPool(IPool _address) public onlyOwner {
        emit UpdateLendingPool(lendingPool, _address);
        lendingPool = _address;
    }

    /// @notice Only owner. Update PriceOracle address
    /// @param _address New address
    function updatePriceOracle(IPriceOracleGetter _address) public onlyOwner {
        emit UpdatePriceOracle(priceOracle, _address);
        priceOracle = _address;
    }

    /// @notice Only owner. Update ProtocolDataProvider address
    /// @param _address New address
    function updateProtocolDataProvider(IProtocolDataProviderV3 _address) public onlyOwner {
        emit UpdateProtocolDataProvider(protocolDataProvider, _address);
        protocolDataProvider = _address;
    }

    /// @notice Only owner. Update RewardsController address
    /// @param _address New address
    function updateRewardsController(IRewardsControllerV3 _address) public onlyOwner {
        emit UpdateRewardsController(rewardsController, _address);
        rewardsController = _address;
    }

    /* View */

    /// @notice Fetches config parameters.
    /// @return config config parameters.
    function getConfig() public view returns (LoanConfig memory config) {
        config = loanConfig;
    }

    /// @notice Fetches prices from Aave Price Oracle.
    /// @return supplyTokenPrice supply token price.
    /// @return borrowTokenPrice borrow token price.
    function getPrices() public view returns (uint256 supplyTokenPrice, uint256 borrowTokenPrice){
        address[] memory tokens = new address[](2);
        uint256[] memory prices = new uint256[](2);
        tokens[0] = loanConfig.supplyAddress;
        tokens[1] = loanConfig.borrowAddress;
        prices = priceOracle.getAssetsPrices(tokens);
        return (prices[0], prices[1]);
    }

    /// @notice Fetches health factor from pool.
    /// @return healthFactor health factor.
    function getHealthFactor() public view returns (uint256 healthFactor){
        (, , , , , healthFactor) = lendingPool.getUserAccountData(address(this));
    }

    /// @notice Fetches liquidity and shortfall from pool.
    /// @return liquidity Liquidity (or 0 if undercollateralized).
    /// @return shortfall Shortfall (or 0 if overcollateralized).
    function getLiquidityAndShortFall() public view returns (uint256 liquidity, uint256 shortfall){
        (liquidity, shortfall) = getLiquidityAndShortFallAsBase();
        liquidity = _baseToAsset(loanConfig.supplyAddress, liquidity);
        shortfall = _baseToAsset(loanConfig.borrowAddress, shortfall);
    }

    /// @notice Fetches amount of token supplied to pool.
    /// @return supplied Amount of token supplied to pool.
    function getSupply() public view returns (uint256 supplied){
        (address atoken, ,) = _getAaveTokens(loanConfig.supplyAddress);
        supplied = _erc20Balance(atoken);
    }

    /// @notice Fetches amount of token borrowed from pool.
    /// @return borrowed Amount of token borrowed from pool.
    function getBorrow() public view returns (uint256 borrowed){
        (, , address variableDebtToken) = _getAaveTokens(loanConfig.borrowAddress);
        borrowed = _erc20Balance(variableDebtToken);
    }

    /// @notice Fetches the current supply rate of the configured supply market.
    /// @return supplyRate Supply rate.
    function getSupplyRate() public view returns (uint256 supplyRate){
        supplyRate = _getSupplyRate();
    }

    /// @notice Fetches the current borrow rate of the configured borrow market.
    /// @return borrowRate Borrow rate.
    function getBorrowRate() public view returns (uint256 borrowRate){
        borrowRate = _getBorrowRate();
    }

    /// @notice Aggregates and returns position data.
    /// @return healthFactor Health factor.
    /// @return liquidity Liquidity (or 0 if undercollateralized).
    /// @return shortfall Shortfall (or 0 if overcollateralized).
    /// @return supplied Amount of tokens supplied to pool.
    /// @return borrowed Amount of tokens borrowed from pool.
    /// @return supplyRate Supply rate of the configured supply market.
    /// @return borrowRate Borrow rate of the configured borrow market.
    function getPositionDataSnapshot() public view returns (uint256 healthFactor, uint256 liquidity, uint256 shortfall, uint256 supplied, uint256 borrowed, uint256 supplyRate, uint256 borrowRate){
        healthFactor = getHealthFactor();
        (liquidity, shortfall) = getLiquidityAndShortFall();
        supplied = getSupply();
        borrowed = getBorrow();
        supplyRate = getSupplyRate();
        borrowRate = getBorrowRate();
    }

    /// @notice Fetches total supply, debt and current price of the configured markets.
    /// @return supplyMarketDeposits Total supply of the configured supply market.
    /// @return supplyMarketDebt Total debt of the configured supply market.
    /// @return supplyMarketAssetPrice Price of the configured supply market underlying.
    /// @return borrowMarketDeposits Total supply of the configured borrow market.
    /// @return borrowMarketDebt Total debt of the configured borrow market.
    /// @return borrowMarketAssetPrice Price of the configured borrow market underlying.
    function getMarketsData() public view returns (uint256 supplyMarketDeposits, uint256 supplyMarketDebt, uint256 supplyMarketAssetPrice, uint256 borrowMarketDeposits, uint256 borrowMarketDebt, uint256 borrowMarketAssetPrice){
        (supplyMarketDeposits, supplyMarketDebt, borrowMarketDeposits, borrowMarketDebt) = _getMarketsData();
        (supplyMarketAssetPrice, borrowMarketAssetPrice ) = getPrices();
    }

    /// @notice Fetches amount of token supplied to pool as base.
    /// @return supplyAsBase Amount of token supplied to pool as base.
    function getSupplyAsBase() public view returns (uint256 supplyAsBase){
        (supplyAsBase, , , , ,) = lendingPool.getUserAccountData(address(this));
    }

    /// @notice Fetches amount of token borrowed from pool as base.
    /// @return borrowAsBase Amount of token borrowed from pool as base.
    function getBorrowAsBase() public view returns (uint256 borrowAsBase){
        (, borrowAsBase, , , ,) = lendingPool.getUserAccountData(address(this));
    }

    /// @notice Fetches liquidity and shortfall from pool as base.
    /// @return liquidity Liquidity (or 0 if undercollateralized) as base.
    /// @return shortfall Shortfall (or 0 if overcollateralized) as base.
    function getLiquidityAndShortFallAsBase() public view returns (uint256 liquidity, uint256 shortfall){
        (uint256 supplied, uint256 borrowed, , , uint256 ltv, ) = lendingPool.getUserAccountData(address(this));
        uint256 borrow_power = supplied * ltv / 1e4;
        liquidity = borrow_power > borrowed ? borrow_power - borrowed : 0;
        shortfall = borrow_power < borrowed ? borrowed - borrow_power : 0;
    }

    /// @notice Fetches collateral factor for the given collateral.
    /// @param _collateral Address of the collateral.
    /// @return Collateral factor.
    function getLiquidationThreshold(address _collateral) public view returns (uint256){
        (, , uint256 liquidationThreshold, , , , , , ,) = protocolDataProvider.getReserveConfigurationData(_collateral);
        return liquidationThreshold * 1e18 / 1e4;
    }

    /* Primitives */

    /// @notice Only executor. Supplies the given amount of supply token to pool.
    /// @param _amount Amount of token to supply.
    function supply(uint256 _amount) public onlyExecutor {
        _checkAllowanceAndApprove(loanConfig.supplyAddress, address(lendingPool), _amount);
        _supply(_amount);
        _logSnapshot(SnapshotUpdateAction.SUPPLY, _amount);
    }

    /// @notice Only executor. Borrows the given amount of borrow token from pool.
    /// @param _amount Amount of token to borrow.
    function borrow(uint256 _amount) public onlyExecutor healthFactorCheck {
        _borrow(_amount);
        _logSnapshot(SnapshotUpdateAction.BORROW, _amount);
    }

    /// @notice Only executor. Repays the given amount of borrow token debt to pool.
    /// @param _amount Amount of token debt to repay.
    function repay(uint256 _amount) public onlyExecutor {
        _repay(_amount);
        _logSnapshot(SnapshotUpdateAction.REPAY, _amount);
    }

    /// @notice Only executor. Repays all borrow token debt.
    function repayAll() public onlyExecutor {
        uint256 totalBorrow = getBorrow();
        if (totalBorrow == 0)
            return;
        _repay(type(uint).max);
        _approveToken(loanConfig.borrowAddress, address(lendingPool), 0);
        _logSnapshot(SnapshotUpdateAction.REPAY, totalBorrow);
    }

    /// @notice Only executor. Withdraws the given amount of underlying from pool.
    /// @param _amount Amount of underlying to withdraw.
    function withdrawSupply(uint256 _amount) public onlyExecutor healthFactorCheck {
        _withdrawSupply(_amount);
        _logSnapshot(SnapshotUpdateAction.WITHDRAW, _amount);
    }

    /// @notice Only executor. Withdraws all underlying from pool.
    function withdrawSupplyAll() public onlyExecutor healthFactorCheck {
        uint256 totalSupply = getSupply();
        _withdrawSupply(type(uint).max);
        _logSnapshot(SnapshotUpdateAction.WITHDRAW, totalSupply);
    }

    /// @notice Only executor. Repays all borrow token debt and withdraws all underlying from pool.
    /// @return amountWithdrawn Amount of tokens withdrawn.
    function repayAllAndWithdrawSupplyAll() public onlyExecutor healthFactorCheck returns (uint256 amountWithdrawn){
        repayAll();
        uint256 previousBalance = _erc20Balance(loanConfig.supplyAddress);
        withdrawSupplyAll();
        amountWithdrawn = _erc20Balance(loanConfig.supplyAddress) - previousBalance;
    }

    /// @notice Only executor. Claims pending rewards for the configured markets.
    /// @return claimedTokens Array of claimed tokens addresses.
    /// @return claimedAmounts Array of claimed amounts.
    function claimRewards() public onlyExecutor hasIncentivesController returns (address[] memory claimedTokens, uint256[] memory claimedAmounts){
        address[] memory tokens = new address[](4);
        (address aToken, , address variableDebtToken) = _getAaveTokens(loanConfig.supplyAddress);
        tokens[0] = aToken;
        tokens[1] = variableDebtToken;
        (address aTokenB, , address variableDebtTokenB) = _getAaveTokens(loanConfig.borrowAddress);
        tokens[2] = aTokenB;
        tokens[3] = variableDebtTokenB;
        (claimedTokens, claimedAmounts) = _claimRewards(tokens);
        emit ClaimRewards(claimedTokens, claimedAmounts);
    }

    /* Interfaces for Harvester */
    
    /// @notice Only executor. Supply tokens to the configured market.
    /// @param _amount Amount to supply.
    /// @dev For Harvester compatibility.
    function deposit(address, uint _amount) external virtual onlyExecutor {
        supply(_amount);
    }

    /// @notice Only executor. Claim rewards across all markets.
    /// @dev For Harvester compatibility.
    function claimRewards(address, uint) external virtual onlyExecutor hasIncentivesController returns (uint){
        (, uint[] memory claimedAmounts) = claimRewards();
        return claimedAmounts[0];
    }
}

/* SPDX-License-Identifier: UNLICENSED */
pragma solidity ^0.8.20;

import './ITBContract.sol';

interface ILoanManager {
    struct LoanConfig {
        address supplyAddress;
        address borrowAddress;
        uint minHealthFactor;
    }

    function getConfig() external view returns (LoanConfig memory);

    function setConfig(address supplyAddress, address borrowAddress, uint minHealthFactor) external;

    function supply(uint amount) external;

    function borrow(uint amount) external;

    function repay(uint256 _amount) external;

    function repayAll() external;

    function withdrawSupply(uint256 _amount) external;

    function withdrawSupplyAll() external;

    function repayAllAndWithdrawSupplyAll() external returns (uint256);

    function getPrices() external view returns (uint256, uint256);

    function getHealthFactor() external view returns (uint256);

    function getLiquidityAndShortFall() external view returns (uint256, uint256);

    function getSupply() external view returns (uint256);

    function getBorrow() external view returns (uint256);

    function getSupplyRate() external view returns (uint256);

    function getBorrowRate() external view returns (uint256);

    function getPositionDataSnapshot() external view returns (uint256, uint256, uint256, uint256, uint256, uint256, uint256);

    function claimRewards() external returns (address[] memory, uint256[] memory);

    function getMarketsData() external returns (uint256, uint256, uint256, uint256, uint256, uint256);

    function getLiquidationThreshold(address _collateral) external view returns (uint256);
}

/* SPDX-License-Identifier: UNLICENSED */
pragma solidity ^0.8.20;

import './utils/Withdrawable.sol';
import './utils/IWETH.sol';


/// @title ITBContract contract that implements common owner only functions accros all strategies
/// @author IntoTheBlock Corp
/// @dev Abstract
abstract contract ITBContract is Withdrawable {
    using SafeERC20 for IERC20;
    event ApproveToken(address indexed token, address guy, uint256 wad);
    address payable immutable public WNATIVE;

    uint constant ONE = 1e18;

    /// @param _executors Executor addresses
    constructor(address[] memory _executors, address payable _wnative) Executable(_executors) {
        WNATIVE = _wnative;
    }

    function _percentageAmount(uint _amount, uint _percentage) internal pure returns (uint) {
        return _amount * _percentage / ONE;
    }

    /// @notice Set allowance for a given token, amount and spender
    /// @param _token Token to spend
    /// @param _guy Spender
    /// @param _wad Max amount to spend
    function _approveToken(address _token, address _guy, uint256 _wad) internal {
        if (_wad != 0)
            IERC20(_token).forceApprove(_guy, _wad);
        else
            IERC20(_token).approve(_guy, _wad);
        emit ApproveToken(_token, _guy, _wad);
    }

    /// @notice For a given token, amount and spender, check if contract can spend amount and, if necessary, set the allowance to amount
    /// @param _token Token to spend
    /// @param _guy Spender
    /// @param _amount Amount to spend
    function _checkAllowanceAndApprove(address _token, address _guy, uint256 _amount) internal {
        _checkAllowanceAndApprove(_token, _guy, _amount, _amount);
    }

    /// @notice For a given token, amount and spender, check if contract can spend amount and, if necessary, set the allowance to approval amount
    /// @param _token Token to spend
    /// @param _guy Spender
    /// @param _amount Amount to spend
    /// @param _approval_amount Amount to approve
    function _checkAllowanceAndApprove(address _token, address _guy, uint256 _amount, uint256 _approval_amount) internal {
        if (IERC20(_token).allowance(address(this), _guy) < _amount)
            _approveToken(_token, _guy, _approval_amount);
    }

    /// @notice Only owner. Set allowance for a given token, amount and spender
    /// @param _token Token to spend
    /// @param _guy Spender
    /// @param _wad Max amount to spend
    function approveToken(address _token, address _guy, uint256 _wad) external onlyOwner {
        _approveToken(_token, _guy, _wad);
    }

    /// @notice Only owner. Revoke allowance for a given token and spender
    /// @param _token Token to spend
    /// @param _guy Spender
    function revokeToken(address _token, address _guy) external onlyOwner {
        _approveToken(_token, _guy, 0);
    }

    /// @notice Only owner. Execute an arbitrary call
    /// @param _to Target address
    /// @param _value Value (i. e. msg.value)
    /// @param _data Invocation data
    function execute(address _to, uint256 _value, bytes calldata _data) external payable onlyOwner {
        (bool success, bytes memory returnData) = _to.call{ value: _value }(_data);
        require(success, string(returnData));
    }

    /// @notice Only owner. Execute multiple arbitrary calls in order
    /// @param _tos Target address for each call
    /// @param _values Value for each call (i. e. msg.value)
    /// @param _datas Invocation data for each call
    function batchExecute(address[] calldata _tos, uint256[] calldata _values, bytes[] calldata _datas) external payable onlyOwner {
        require(_tos.length == _values.length && _tos.length == _datas.length, "Arguments length mismatch");
        for (uint256 i = 0; i < _tos.length; i++) {
            (bool success, bytes memory returnData) = _tos[i].call{ value: _values[i] }(_datas[i]);
            require(success, string(returnData));
        }
    }

    function wrapNative(uint256 _amount) public onlyExecutor {
        IWETH(WNATIVE).deposit{ value: _amount }();
    }

    function unwrapNative(uint256 _amount) public onlyExecutor {
        IWETH(WNATIVE).withdraw(_amount);
    }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/access/Ownable2Step.sol";

/// @title Base contract that implements executor related functions
/// @author IntoTheBlock Corp
/// @dev Abstract
abstract contract Executable is Ownable2Step {
    mapping(address => bool) public executors;

    event ExecutorUpdated(address indexed executor, bool enabled);

    /// @param _executors Initial whitelisted executor addresses
    constructor(address[] memory _executors) Ownable(msg.sender) {
        for (uint256 i = 0; i < _executors.length; i++) {
            addExecutor(_executors[i]);
        }
    }

    /// @notice Revert if call is not being made from the owner or an executor
    modifier onlyExecutor() {
        require(owner() == msg.sender || executors[msg.sender], "Executable: caller is not the executor");
        _;
    }

    /// @notice Only owner. Add an executor
    /// @param _executor New executor address
    function addExecutor(address _executor) public onlyOwner {
        emit ExecutorUpdated(_executor, true);
        executors[_executor] = true;
    }

    /// @notice Only owner. Remove an executor
    /// @param _executor Executor address to remove
    function removeExecutor(address _executor) external onlyOwner {
        emit ExecutorUpdated(_executor, false);
        executors[_executor] = false;
    }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

interface IWETH {
    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function decimals() external view returns (uint8);

    event Approval(address indexed src, address indexed guy, uint256 wad);
    event Transfer(address indexed src, address indexed dst, uint256 wad);
    event Deposit(address indexed dst, uint256 wad);
    event Withdrawal(address indexed src, uint256 wad);

    function balanceOf(address) external view returns (uint256);
    function allowance(address, address) external view returns (uint256);

    fallback() external payable;
    receive() external payable;
    function deposit() external payable;
    function withdraw(uint256 wad) external;
    function totalSupply() external view returns (uint256);
    function approve(address guy, uint256 wad) external returns (bool);
    function transfer(address dst, uint256 wad) external returns (bool);
    function transferFrom(address src, address dst, uint256 wad) external returns (bool);
}

File 13 of 26 : Ownable2StepWithShortcut.sol
/* SPDX-License-Identifier: UNLICENSED */
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/access/Ownable2Step.sol";

abstract contract Ownable2StepWithShortcut is Ownable2Step {
    function transferOwnership1Step(address newOwner) public onlyOwner {
        require(newOwner != address(0), "ITBOwnable: zero address");
        _transferOwnership(newOwner);
    }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import './Executable.sol';

/**
    Ensures that any contract that inherits from this contract is able to
    withdraw funds that are accidentally received or stuck.
 */

/// @title Base contract that implements withdrawal related functions
/// @author IntoTheBlock Corp
/// @dev Abstract
abstract contract Withdrawable is Executable {
    using SafeERC20 for IERC20;
    address constant ETHER = address(0);

    event LogWithdraw(
        address indexed _to,
        address indexed _asset_address,
        uint256 amount
    );

    receive() external payable {}

    
    /// @notice ERC20 or ETH balance of this contract given a token address
    /// @param _asset_address Token address or address(0) for ETH
    /// @return Balance    
    function _erc20OrNativeBalance(address _asset_address) internal view returns (uint256) {
        return _asset_address == ETHER ? _nativeBalance() : _erc20Balance(_asset_address);
    }
    
    function _erc20Balance(address _asset_address) internal view returns (uint256) {
        return IERC20(_asset_address).balanceOf(address(this));
    }

    function _nativeBalance() internal view returns (uint256) {
        return address(this).balance;
    }
    
    /// @notice ERC20 balance of given account
    /// @param _asset_address Token address 
    /// @param _account Account address 
    /// @return Balance  
    function balanceOf(address _asset_address, address _account) public view returns (uint256) {
        return IERC20(_asset_address).balanceOf(_account);
    }

    /// @notice Send the given amount of the given token or ETH to the given receiver
    /// @param _asset_address Token address or address(0) for ETH
    /// @param _amount Amount to send
    /// @param _to Receiver address
    function _withdraw_to(address _asset_address, uint256 _amount, address payable _to) internal {
        require(_to != address(0), 'Invalid address');
        uint256 balance = _erc20OrNativeBalance(_asset_address);
        require(balance >= _amount, 'Insufficient funds');
        if (_asset_address == ETHER) {
            (bool success, ) = _to.call{value: _amount}(''); /* carry gas over so it works with contracts with custom fallback, we dont care about reentrancy on onlyOwner */
            require(success, 'Native transfer failed.');
        } else
            IERC20(_asset_address).safeTransfer(_to, _amount);
        emit LogWithdraw(_to, _asset_address, _amount);
    }

    /// @notice Only owner. Send the given amount of the given token or ETH to the caller
    /// @param _asset_address Token address or address(0) for ETH
    /// @param _amount Amount to send
    function withdraw(address _asset_address, uint256 _amount) external onlyOwner {
        _withdraw_to(_asset_address, _amount, payable(msg.sender));
    }

    /// @notice Only owner. Send the given amount of the given token or ETH to the given receiver
    /// @param _asset_address Token address or address(0) for ETH
    /// @param _amount Amount to send
    /// @param _to Receiver address
    function withdrawTo(address _asset_address, uint256 _amount, address payable _to) external onlyOwner {
        _withdraw_to(_asset_address, _amount, _to);
    }

    /// @notice Only owner. Send its entire balance of the given token or ETH to the caller
    /// @param _asset_address Token address or address(0) for ETH
    function withdrawAll(address _asset_address) external onlyOwner {
        uint256 balance = _erc20OrNativeBalance(_asset_address);
        _withdraw_to(_asset_address, balance, payable(msg.sender));
    }

    /// @notice Only owner. Send its entire balance of the given token or ETH to the given receiver
    /// @param _asset_address Token address or address(0) for ETH
    /// @param _to Receiver address
    function withdrawAllTo(address _asset_address, address payable _to) external onlyOwner {
        uint256 balance = _erc20OrNativeBalance(_asset_address);
        _withdraw_to(_asset_address, balance, _to);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/Ownable2Step.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Contract module which provides access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * This extension of the {Ownable} contract includes a two-step mechanism to transfer
 * ownership, where the new owner must call {acceptOwnership} in order to replace the
 * old one. This can help prevent common mistakes, such as transfers of ownership to
 * incorrect accounts, or to contracts that are unable to interact with the
 * permission system.
 *
 * The initial owner is specified at deployment time in the constructor for `Ownable`. This
 * can later be changed with {transferOwnership} and {acceptOwnership}.
 *
 * This module is used through inheritance. It will make available all functions
 * from parent (Ownable).
 */
abstract contract Ownable2Step is Ownable {
    address private _pendingOwner;

    event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Returns the address of the pending owner.
     */
    function pendingOwner() public view virtual returns (address) {
        return _pendingOwner;
    }

    /**
     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
     * Can only be called by the current owner.
     *
     * Setting `newOwner` to the zero address is allowed; this can be used to cancel an initiated ownership transfer.
     */
    function transferOwnership(address newOwner) public virtual override onlyOwner {
        _pendingOwner = newOwner;
        emit OwnershipTransferStarted(owner(), newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual override {
        delete _pendingOwner;
        super._transferOwnership(newOwner);
    }

    /**
     * @dev The new owner accepts the ownership transfer.
     */
    function acceptOwnership() public virtual {
        address sender = _msgSender();
        if (pendingOwner() != sender) {
            revert OwnableUnauthorizedAccount(sender);
        }
        _transferOwnership(sender);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/draft-IERC6093.sol)
pragma solidity >=0.8.4;

/**
 * @dev Standard ERC-20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens.
 */
interface IERC20Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC20InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC20InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     * @param allowance Amount of tokens a `spender` is allowed to operate with.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC20InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC20InvalidSpender(address spender);
}

/**
 * @dev Standard ERC-721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
    error ERC721InvalidOwner(address owner);

    /**
     * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
    error ERC721NonexistentToken(uint256 tokenId);

    /**
     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC721InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC721InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
    error ERC721InsufficientApproval(address operator, uint256 tokenId);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC721InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC721InvalidOperator(address operator);
}

/**
 * @dev Standard ERC-1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens.
 */
interface IERC1155Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     * @param tokenId Identifier number of a token.
     */
    error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC1155InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC1155InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param owner Address of the current owner of a token.
     */
    error ERC1155MissingApprovalForAll(address operator, address owner);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC1155InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC1155InvalidOperator(address operator);

    /**
     * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
     * Used in batch transfers.
     * @param idsLength Length of the array of token identifiers
     * @param valuesLength Length of the array of token amounts
     */
    error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC1363.sol)

pragma solidity >=0.6.2;

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

/**
 * @title IERC1363
 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
 *
 * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
 * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
 */
interface IERC1363 is IERC20, IERC165 {
    /*
     * Note: the ERC-165 identifier for this interface is 0xb0202a11.
     * 0xb0202a11 ===
     *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
     *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
     */

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @param data Additional data with no specified format, sent in call to `spender`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}

File 19 of 26 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC165.sol)

pragma solidity >=0.4.16;

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

File 20 of 26 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC20.sol)

pragma solidity >=0.4.16;

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC-20
 * applications.
 */
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
    mapping(address account => uint256) private _balances;

    mapping(address account => mapping(address spender => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * Both values are immutable: they can only be set once during construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the default value returned by this function, unless
     * it's overridden.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual returns (uint8) {
        return 18;
    }

    /// @inheritdoc IERC20
    function totalSupply() public view virtual returns (uint256) {
        return _totalSupply;
    }

    /// @inheritdoc IERC20
    function balanceOf(address account) public view virtual returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `value`.
     */
    function transfer(address to, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, value);
        return true;
    }

    /// @inheritdoc IERC20
    function allowance(address owner, address spender) public view virtual returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, value);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Skips emitting an {Approval} event indicating an allowance update. This is not
     * required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `value`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `value`.
     */
    function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, value);
        _transfer(from, to, value);
        return true;
    }

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _transfer(address from, address to, uint256 value) internal {
        if (from == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        if (to == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(from, to, value);
    }

    /**
     * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
     * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
     * this function.
     *
     * Emits a {Transfer} event.
     */
    function _update(address from, address to, uint256 value) internal virtual {
        if (from == address(0)) {
            // Overflow check required: The rest of the code assumes that totalSupply never overflows
            _totalSupply += value;
        } else {
            uint256 fromBalance = _balances[from];
            if (fromBalance < value) {
                revert ERC20InsufficientBalance(from, fromBalance, value);
            }
            unchecked {
                // Overflow not possible: value <= fromBalance <= totalSupply.
                _balances[from] = fromBalance - value;
            }
        }

        if (to == address(0)) {
            unchecked {
                // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
                _totalSupply -= value;
            }
        } else {
            unchecked {
                // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
                _balances[to] += value;
            }
        }

        emit Transfer(from, to, value);
    }

    /**
     * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
     * Relies on the `_update` mechanism
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _mint(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(address(0), account, value);
    }

    /**
     * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
     * Relies on the `_update` mechanism.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead
     */
    function _burn(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        _update(account, address(0), value);
    }

    /**
     * @dev Sets `value` as the allowance of `spender` over the `owner`'s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     *
     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
     */
    function _approve(address owner, address spender, uint256 value) internal {
        _approve(owner, spender, value, true);
    }

    /**
     * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
     *
     * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
     * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
     * `Approval` event during `transferFrom` operations.
     *
     * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
     * true using the following override:
     *
     * ```solidity
     * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
     *     super._approve(owner, spender, value, true);
     * }
     * ```
     *
     * Requirements are the same as {_approve}.
     */
    function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
        if (owner == address(0)) {
            revert ERC20InvalidApprover(address(0));
        }
        if (spender == address(0)) {
            revert ERC20InvalidSpender(address(0));
        }
        _allowances[owner][spender] = value;
        if (emitEvent) {
            emit Approval(owner, spender, value);
        }
    }

    /**
     * @dev Updates `owner`'s allowance for `spender` based on spent `value`.
     *
     * Does not update the allowance value in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Does not emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance < type(uint256).max) {
            if (currentAllowance < value) {
                revert ERC20InsufficientAllowance(spender, currentAllowance, value);
            }
            unchecked {
                _approve(owner, spender, currentAllowance - value, false);
            }
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity >=0.6.2;

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

/**
 * @dev Interface for the optional metadata functions from the ERC-20 standard.
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)

pragma solidity >=0.4.16;

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

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

/**
 * @title SafeERC20
 * @dev Wrappers around ERC-20 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 {
    /**
     * @dev An operation with an ERC-20 token failed.
     */
    error SafeERC20FailedOperation(address token);

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

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

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

    /**
     * @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
     */
    function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
        return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
     */
    function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
        return _callOptionalReturnBool(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     *
     * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
     * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
     * set here.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

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

    /**
     * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            safeTransfer(token, to, value);
        } else if (!token.transferAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
     * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferFromAndCallRelaxed(
        IERC1363 token,
        address from,
        address to,
        uint256 value,
        bytes memory data
    ) internal {
        if (to.code.length == 0) {
            safeTransferFrom(token, from, to, value);
        } else if (!token.transferFromAndCall(from, to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
     * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
     * once without retrying, and relies on the returned value to be true.
     *
     * Reverts if the returned value is other than `true`.
     */
    function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            forceApprove(token, to, value);
        } else if (!token.approveAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            // bubble errors
            if iszero(success) {
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, returndatasize())
                revert(ptr, returndatasize())
            }
            returnSize := returndatasize()
            returnValue := mload(0)
        }

        if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0)
        }
        return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

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

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol)

pragma solidity >=0.4.16;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * 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[ERC 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);
}

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

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address[]","name":"_executors","type":"address[]"},{"internalType":"address payable","name":"_wnative","type":"address"},{"internalType":"address","name":"_supplyAddress","type":"address"},{"internalType":"address","name":"_borrowAddress","type":"address"},{"internalType":"uint256","name":"_minHealthFactor","type":"uint256"},{"internalType":"contract IPool","name":"_lendingPool","type":"address"},{"internalType":"contract IPriceOracleGetter","name":"_priceOracle","type":"address"},{"internalType":"contract IProtocolDataProviderV3","name":"_protocolDataProvider","type":"address"},{"internalType":"contract IRewardsControllerV3","name":"_rewardsController","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"guy","type":"address"},{"indexed":false,"internalType":"uint256","name":"wad","type":"uint256"}],"name":"ApproveToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"claimedTokens","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"claimedAmounts","type":"uint256[]"}],"name":"ClaimRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"supplyToken","type":"address"}],"name":"DisableCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"supplyToken","type":"address"}],"name":"EnableCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"executor","type":"address"},{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"ExecutorUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":true,"internalType":"address","name":"_asset_address","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LogWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"emode_category","type":"uint8"}],"name":"SetEMode","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"enum LoanManager.SnapshotUpdateAction","name":"action","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"executor","type":"address"},{"indexed":false,"internalType":"address","name":"supplyToken","type":"address"},{"indexed":false,"internalType":"address","name":"borrowToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"totalSupplied","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalDebt","type":"uint256"}],"name":"SnapshotUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"emode_category","type":"uint8"}],"name":"UpdateEModeCategory","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IPool","name":"oldPool","type":"address"},{"indexed":false,"internalType":"contract IPool","name":"newPool","type":"address"}],"name":"UpdateLendingPool","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"supplyAddress","type":"address"},{"internalType":"address","name":"borrowAddress","type":"address"},{"internalType":"uint256","name":"minHealthFactor","type":"uint256"}],"indexed":false,"internalType":"struct ILoanManager.LoanConfig","name":"oldConfig","type":"tuple"},{"components":[{"internalType":"address","name":"supplyAddress","type":"address"},{"internalType":"address","name":"borrowAddress","type":"address"},{"internalType":"uint256","name":"minHealthFactor","type":"uint256"}],"indexed":false,"internalType":"struct ILoanManager.LoanConfig","name":"newConfig","type":"tuple"}],"name":"UpdateLoanConfig","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IPriceOracleGetter","name":"oldOracle","type":"address"},{"indexed":false,"internalType":"contract IPriceOracleGetter","name":"newOracle","type":"address"}],"name":"UpdatePriceOracle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IProtocolDataProviderV3","name":"oldProvider","type":"address"},{"indexed":false,"internalType":"contract IProtocolDataProviderV3","name":"newProvider","type":"address"}],"name":"UpdateProtocolDataProvider","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IRewardsControllerV3","name":"oldController","type":"address"},{"indexed":false,"internalType":"contract IRewardsControllerV3","name":"newController","type":"address"}],"name":"UpdateRewardsController","type":"event"},{"inputs":[],"name":"WNATIVE","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_executor","type":"address"}],"name":"addExecutor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_guy","type":"address"},{"internalType":"uint256","name":"_wad","type":"uint256"}],"name":"approveToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_asset_address","type":"address"},{"internalType":"address","name":"_account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_tos","type":"address[]"},{"internalType":"uint256[]","name":"_values","type":"uint256[]"},{"internalType":"bytes[]","name":"_datas","type":"bytes[]"}],"name":"batchExecute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"borrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimRewards","outputs":[{"internalType":"address[]","name":"claimedTokens","type":"address[]"},{"internalType":"uint256[]","name":"claimedAmounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"claimRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"disableCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emodeCategory","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"enableCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"execute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"executors","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBorrow","outputs":[{"internalType":"uint256","name":"borrowed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBorrowAsBase","outputs":[{"internalType":"uint256","name":"borrowAsBase","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBorrowRate","outputs":[{"internalType":"uint256","name":"borrowRate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getConfig","outputs":[{"components":[{"internalType":"address","name":"supplyAddress","type":"address"},{"internalType":"address","name":"borrowAddress","type":"address"},{"internalType":"uint256","name":"minHealthFactor","type":"uint256"}],"internalType":"struct ILoanManager.LoanConfig","name":"config","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getHealthFactor","outputs":[{"internalType":"uint256","name":"healthFactor","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_collateral","type":"address"}],"name":"getLiquidationThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLiquidityAndShortFall","outputs":[{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"shortfall","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLiquidityAndShortFallAsBase","outputs":[{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"shortfall","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMarketsData","outputs":[{"internalType":"uint256","name":"supplyMarketDeposits","type":"uint256"},{"internalType":"uint256","name":"supplyMarketDebt","type":"uint256"},{"internalType":"uint256","name":"supplyMarketAssetPrice","type":"uint256"},{"internalType":"uint256","name":"borrowMarketDeposits","type":"uint256"},{"internalType":"uint256","name":"borrowMarketDebt","type":"uint256"},{"internalType":"uint256","name":"borrowMarketAssetPrice","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPositionDataSnapshot","outputs":[{"internalType":"uint256","name":"healthFactor","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"shortfall","type":"uint256"},{"internalType":"uint256","name":"supplied","type":"uint256"},{"internalType":"uint256","name":"borrowed","type":"uint256"},{"internalType":"uint256","name":"supplyRate","type":"uint256"},{"internalType":"uint256","name":"borrowRate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPrices","outputs":[{"internalType":"uint256","name":"supplyTokenPrice","type":"uint256"},{"internalType":"uint256","name":"borrowTokenPrice","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSupply","outputs":[{"internalType":"uint256","name":"supplied","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSupplyAsBase","outputs":[{"internalType":"uint256","name":"supplyAsBase","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSupplyRate","outputs":[{"internalType":"uint256","name":"supplyRate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"loanConfig","outputs":[{"internalType":"address","name":"supplyAddress","type":"address"},{"internalType":"address","name":"borrowAddress","type":"address"},{"internalType":"uint256","name":"minHealthFactor","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_executor","type":"address"}],"name":"removeExecutor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"repay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"repayAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"repayAllAndWithdrawSupplyAll","outputs":[{"internalType":"uint256","name":"amountWithdrawn","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_guy","type":"address"}],"name":"revokeToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_supplyAddress","type":"address"},{"internalType":"address","name":"_borrowAddress","type":"address"},{"internalType":"uint256","name":"_minHealthFactor","type":"uint256"},{"internalType":"uint8","name":"_emode_category","type":"uint8"}],"name":"setConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_supplyAddress","type":"address"},{"internalType":"address","name":"_borrowAddress","type":"address"},{"internalType":"uint256","name":"_minHealthFactor","type":"uint256"}],"name":"setConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setEMode","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"supply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership1Step","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"unwrapNative","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IPool","name":"_address","type":"address"}],"name":"updateLendingPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IPriceOracleGetter","name":"_address","type":"address"}],"name":"updatePriceOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IProtocolDataProviderV3","name":"_address","type":"address"}],"name":"updateProtocolDataProvider","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IRewardsControllerV3","name":"_address","type":"address"}],"name":"updateRewardsController","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_asset_address","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_asset_address","type":"address"}],"name":"withdrawAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_asset_address","type":"address"},{"internalType":"address payable","name":"_to","type":"address"}],"name":"withdrawAllTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdrawSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawSupplyAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_asset_address","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address payable","name":"_to","type":"address"}],"name":"withdrawTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"wrapNative","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

0x60a06040523480156200001157600080fd5b50604051620041b4380380620041b4833981016040819052620000349162000506565b88888888888888888888888133806200006857604051631e4fbdf760e01b8152600060048201526024015b60405180910390fd5b620000738162000123565b5060005b8151811015620000c357620000ae8282815181106200009a576200009a6200066a565b60200260200101516200014160201b60201c565b80620000ba8162000680565b91505062000077565b50506001600160a01b031660805250620000df878787620001ae565b620000ea8462000286565b620000f583620002f9565b62000100826200036c565b6200010b81620003df565b505050505050505050505050505050505050620006a8565b600180546001600160a01b03191690556200013e8162000452565b50565b6200014b620004a2565b604051600181526001600160a01b038216907f9fdbc2d48b8a0db2f62663bf9312ad02f5b1f6414ad600b55a247d09aeec3ea29060200160405180910390a26001600160a01b03166000908152600260205260409020805460ff19166001179055565b620001b8620004a2565b60408051606080820183526001600160a01b038681168352858116602080850191825284860187815286516007548516815260085485169281019290925260095496820196909652845183169381019390935251166080820152915160a0830152907f5c3e76e88db066780deaf8690f603a580b3254893e1e188bd3ce4f9ae1dc4c3f9060c00160405180910390a18051600780546001600160a01b039283166001600160a01b03199182161790915560208301516008805491909316911617905560400151600955505050565b62000290620004a2565b600454604080516001600160a01b03928316815291831660208301527f4f2328eba313841d245841fd4c1c80ecf36e9102ca4a5f8e9c663fd470ad08a2910160405180910390a1600480546001600160a01b0319166001600160a01b0392909216919091179055565b62000303620004a2565b600354604080516001600160a01b03928316815291831660208301527ff3920b145a63851522088bd18b14d6bb919fbd12ac87f12498d6001c727ba070910160405180910390a1600380546001600160a01b0319166001600160a01b0392909216919091179055565b62000376620004a2565b600554604080516001600160a01b03928316815291831660208301527f4a9f6afb8ebccedd27b079bb7eb7671558dabc522819eb7c603e06fbec786833910160405180910390a1600580546001600160a01b0319166001600160a01b0392909216919091179055565b620003e9620004a2565b600654604080516001600160a01b03928316815291831660208301527f495515279f4af7982f79035bbefde974a37f2be9a86595cdeb73385e0efe9eee910160405180910390a1600680546001600160a01b0319166001600160a01b0392909216919091179055565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000546001600160a01b03163314620004d15760405163118cdaa760e01b81523360048201526024016200005f565b565b634e487b7160e01b600052604160045260246000fd5b80516001600160a01b03811681146200050157600080fd5b919050565b60008060008060008060008060006101208a8c0312156200052657600080fd5b89516001600160401b03808211156200053e57600080fd5b818c0191508c601f8301126200055357600080fd5b815181811115620005685762000568620004d3565b8060051b604051601f19603f83011681018181108582111715620005905762000590620004d3565b6040529182526020808301935084820101918f831115620005b057600080fd5b6020850194505b82851015620005dd57620005cb85620004e9565b845260209485019490930192620005b7565b809d505050505050620005f360208b01620004e9565b97506200060360408b01620004e9565b96506200061360608b01620004e9565b955060808a015194506200062a60a08b01620004e9565b93506200063a60c08b01620004e9565b92506200064a60e08b01620004e9565b91506200065b6101008b01620004e9565b90509295985092959850929598565b634e487b7160e01b600052603260045260246000fd5b600060018201620006a157634e487b7160e01b600052601160045260246000fd5b5060010190565b608051613ae2620006d26000396000818161083a01528181610eaf01526117990152613ae26000f3fe60806040526004361061036f5760003560e01c80639a99b4f0116101c6578063ccbefbd5116100f7578063f2fde38b11610095578063f7888aec1161006f578063f7888aec14610a69578063f8d3a4b814610a89578063fa09e63014610a9e578063fa3ae6dc14610abe57600080fd5b8063f2fde38b14610a09578063f3fef3a314610a29578063f66c5f2a14610a4957600080fd5b8063e1b97139116100d1578063e1b97139146109a1578063e30c3978146109c1578063e38464f2146109df578063ee9af25d146109f457600080fd5b8063ccbefbd514610957578063cf12fa291461096c578063da3e33971461098157600080fd5b8063b61d27f611610164578063bd9a548b1161013e578063bd9a548b146108a4578063c3f909d4146108b9578063c4e2c1e614610917578063c5ebeaec1461093757600080fd5b8063b61d27f61461085c578063b6703fcd1461086f578063ba1c5e801461088f57600080fd5b80639c448049116101a05780639c4480491461079f578063a5f352b7146107c9578063a9f3025e146107de578063b381cf401461082857600080fd5b80639a99b4f01461072a5780639ac2a0111461074a5780639b09dd621461078a57600080fd5b80634603fd89116102a05780637b86e42c1161023e5780638da5cb5b116102185780638da5cb5b14610698578063902835f6146106ca5780639169d833146106ea5780639504ad6d1461070a57600080fd5b80637b86e42c1461063757806384bdc9a81461065757806387ba901e1461066c57600080fd5b8063578b92a61161027a578063578b92a6146105b65780636c9c2faf146105f8578063715018a61461060d57806379ba50971461062257600080fd5b80634603fd891461055357806347e7ef24146105735780634a96689a1461059357600080fd5b806334b10a6d1161030d578063371fd8e6116102e7578063371fd8e6146104db578063372500ab146104fb57806339d1fc821461051e5780633a2c08a71461053e57600080fd5b806334b10a6d1461047b578063354030231461049b578063371f30ca146104bb57600080fd5b8063247884291161034957806324788429146103d05780632a1d7543146103f05780632ec8cd8c146104465780633419ba231461045b57600080fd5b8063077d97d71461037b5780630b9b1f9a146103905780631f5a0bbe146103b057600080fd5b3661037657005b600080fd5b61038e610389366004612fb8565b610ad3565b005b34801561039c57600080fd5b5061038e6103ab366004613067565b610c49565b3480156103bc57600080fd5b5061038e6103cb366004613067565b610cba565b3480156103dc57600080fd5b5061038e6103eb366004613067565b610d25565b3480156103fc57600080fd5b5060075460085460095461041b926001600160a01b0390811692169083565b604080516001600160a01b039485168152939092166020840152908201526060015b60405180910390f35b34801561045257600080fd5b5061038e610d8d565b34801561046757600080fd5b5061038e610476366004613084565b610e2e565b34801561048757600080fd5b5061038e6104963660046130bd565b610e46565b3480156104a757600080fd5b5061038e6104b63660046130bd565b610f17565b3480156104c757600080fd5b5061038e6104d63660046130e5565b610f9e565b3480156104e757600080fd5b5061038e6104f63660046130bd565b611000565b34801561050757600080fd5b50610510611067565b60405161043d92919061317c565b34801561052a57600080fd5b5061038e610539366004613067565b61126a565b34801561054a57600080fd5b5061038e6112db565b34801561055f57600080fd5b5061038e61056e366004613067565b611399565b34801561057f57600080fd5b5061038e61058e3660046131d3565b61140a565b34801561059f57600080fd5b506105a8611466565b60405190815260200161043d565b3480156105c257600080fd5b506105cb611538565b604080519687526020870195909552938501929092526060840152608083015260a082015260c00161043d565b34801561060457600080fd5b506105a861156a565b34801561061957600080fd5b5061038e611597565b34801561062e57600080fd5b5061038e6115a9565b34801561064357600080fd5b506105a8610652366004613067565b6115ea565b34801561066357600080fd5b506105a8611690565b34801561067857600080fd5b50600a546106869060ff1681565b60405160ff909116815260200161043d565b3480156106a457600080fd5b506000546001600160a01b03165b6040516001600160a01b03909116815260200161043d565b3480156106d657600080fd5b5061038e6106e5366004613213565b61169f565b3480156106f657600080fd5b5061038e6107053660046130bd565b611744565b34801561071657600080fd5b5061038e6107253660046130bd565b61180e565b34801561073657600080fd5b506105a86107453660046131d3565b61189e565b34801561075657600080fd5b5061077a610765366004613067565b60026020526000908152604090205460ff1681565b604051901515815260200161043d565b34801561079657600080fd5b5061038e611962565b3480156107ab57600080fd5b506107b4611a18565b6040805192835260208301919091520161043d565b3480156107d557600080fd5b506105a8611af4565b3480156107ea57600080fd5b506107f3611b70565b604080519788526020880196909652948601939093526060850191909152608084015260a083015260c082015260e00161043d565b34801561083457600080fd5b506106b27f000000000000000000000000000000000000000000000000000000000000000081565b61038e61086a366004613254565b611bc3565b34801561087b57600080fd5b5061038e61088a366004613084565b611c50565b34801561089b57600080fd5b506105a8611c75565b3480156108b057600080fd5b506107b4611c7f565b3480156108c557600080fd5b5060408051606080820183526000808352602080840182905292840152825190810183526007546001600160a01b039081168252600854169181019190915260095481830152905161043d91906132dd565b34801561092357600080fd5b5061038e61093236600461330c565b611ddf565b34801561094357600080fd5b5061038e6109523660046130bd565b611df2565b34801561096357600080fd5b506107b4611e59565b34801561097857600080fd5b506105a8611e9e565b34801561098d57600080fd5b5061038e61099c366004613213565b611f1b565b3480156109ad57600080fd5b5061038e6109bc366004613067565b611f2e565b3480156109cd57600080fd5b506001546001600160a01b03166106b2565b3480156109eb57600080fd5b506105a8611f8c565b348015610a0057600080fd5b506105a8612009565b348015610a1557600080fd5b5061038e610a24366004613067565b612030565b348015610a3557600080fd5b5061038e610a443660046131d3565b6120a1565b348015610a5557600080fd5b5061038e610a64366004613067565b6120b4565b348015610a7557600080fd5b506105a8610a84366004613084565b612125565b348015610a9557600080fd5b5061038e612193565b348015610aaa57600080fd5b5061038e610ab9366004613067565b612238565b348015610aca57600080fd5b5061038e612258565b610adb6122eb565b8483148015610ae957508481145b610b3a5760405162461bcd60e51b815260206004820152601960248201527f417267756d656e7473206c656e677468206d69736d617463680000000000000060448201526064015b60405180910390fd5b60005b85811015610c4057600080888884818110610b5a57610b5a61334e565b9050602002016020810190610b6f9190613067565b6001600160a01b0316878785818110610b8a57610b8a61334e565b90506020020135868686818110610ba357610ba361334e565b9050602002810190610bb59190613364565b604051610bc39291906133ab565b60006040518083038185875af1925050503d8060008114610c00576040519150601f19603f3d011682016040523d82523d6000602084013e610c05565b606091505b5091509150818190610c2a5760405162461bcd60e51b8152600401610b3191906133bb565b5050508080610c389061341f565b915050610b3d565b50505050505050565b610c516122eb565b600454604080516001600160a01b03928316815291831660208301527f4f2328eba313841d245841fd4c1c80ecf36e9102ca4a5f8e9c663fd470ad08a2910160405180910390a1600480546001600160a01b0319166001600160a01b0392909216919091179055565b610cc26122eb565b604051600181526001600160a01b038216907f9fdbc2d48b8a0db2f62663bf9312ad02f5b1f6414ad600b55a247d09aeec3ea29060200160405180910390a26001600160a01b03166000908152600260205260409020805460ff19166001179055565b610d2d6122eb565b604051600081526001600160a01b038216907f9fdbc2d48b8a0db2f62663bf9312ad02f5b1f6414ad600b55a247d09aeec3ea29060200160405180910390a26001600160a01b03166000908152600260205260409020805460ff19169055565b33610da06000546001600160a01b031690565b6001600160a01b03161480610dc457503360009081526002602052604090205460ff165b610de05760405162461bcd60e51b8152600401610b3190613438565b6000610dea61156a565b9050610df7600019612318565b610e0260038261239a565b50600954610e0e611af4565b1015610e2c5760405162461bcd60e51b8152600401610b319061347e565b565b610e366122eb565b610e4282826000612439565b5050565b33610e596000546001600160a01b031690565b6001600160a01b03161480610e7d57503360009081526002602052604090205460ff165b610e995760405162461bcd60e51b8152600401610b3190613438565b604051632e1a7d4d60e01b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d906024015b600060405180830381600087803b158015610efc57600080fd5b505af1158015610f10573d6000803e3d6000fd5b5050505050565b33610f2a6000546001600160a01b031690565b6001600160a01b03161480610f4e57503360009081526002602052604090205460ff165b610f6a5760405162461bcd60e51b8152600401610b3190613438565b600754600454610f87916001600160a01b03908116911683612518565b610f9081612524565b610f9b60008261239a565b50565b610fa66122eb565b610fb184848461169f565b60405160ff821681527fb1075a43dcab7b1d3b05af43f1bba5f67e9dd2ed68c4bdfee0f83473e208448c9060200160405180910390a1600a805460ff191660ff92909216919091179055505050565b336110136000546001600160a01b031690565b6001600160a01b0316148061103757503360009081526002602052604090205460ff165b6110535760405162461bcd60e51b8152600401610b3190613438565b61105c8161256e565b610f9b60028261239a565b6060803361107d6000546001600160a01b031690565b6001600160a01b031614806110a157503360009081526002602052604090205460ff165b6110bd5760405162461bcd60e51b8152600401610b3190613438565b6006546001600160a01b03166110fa5760405162461bcd60e51b8152602060048201526002602482015261433160f01b6044820152606401610b31565b60408051600480825260a08201909252600091602082016080803683375050600754919250600091829150611137906001600160a01b03166125d5565b925050915081836000815181106111505761115061334e565b60200260200101906001600160a01b031690816001600160a01b03168152505080836001815181106111845761118461334e565b6001600160a01b03928316602091820292909201015260085460009182916111ac91166125d5565b925050915081856002815181106111c5576111c561334e565b60200260200101906001600160a01b031690816001600160a01b03168152505080856003815181106111f9576111f961334e565b60200260200101906001600160a01b031690816001600160a01b03168152505061122285612657565b60405191985096507ffaecd94ee6c58a110cb23850825a20cea2b79260d0ed1eacd9356c39618282bd90611259908990899061317c565b60405180910390a150505050509091565b6112726122eb565b600354604080516001600160a01b03928316815291831660208301527ff3920b145a63851522088bd18b14d6bb919fbd12ac87f12498d6001c727ba070910160405180910390a1600380546001600160a01b0319166001600160a01b0392909216919091179055565b6112e36122eb565b60048054600754604051635a3b74b960e01b81526001600160a01b0391821693810193909352600060248401521690635a3b74b990604401600060405180830381600087803b15801561133557600080fd5b505af1158015611349573d6000803e3d6000fd5b50506007546040516001600160a01b0390911681527fbe3bd67f54846f0ab016cb173a6b7bcdf9e06726594cf1a96faea16bf6d504809250602001905060405180910390a1600954610e0e611af4565b6113a16122eb565b600654604080516001600160a01b03928316815291831660208301527f495515279f4af7982f79035bbefde974a37f2be9a86595cdeb73385e0efe9eee910160405180910390a1600680546001600160a01b0319166001600160a01b0392909216919091179055565b3361141d6000546001600160a01b031690565b6001600160a01b0316148061144157503360009081526002602052604090205460ff165b61145d5760405162461bcd60e51b8152600401610b3190613438565b610e4281610f17565b60003361147b6000546001600160a01b031690565b6001600160a01b0316148061149f57503360009081526002602052604090205460ff165b6114bb5760405162461bcd60e51b8152600401610b3190613438565b6114c3612258565b6007546000906114db906001600160a01b03166126df565b90506114e5610d8d565b60075481906114fc906001600160a01b03166126df565b61150691906134cb565b915050600760020154611517611af4565b10156115355760405162461bcd60e51b8152600401610b319061347e565b90565b60008060008060008061154961274a565b9298509096509350915061155b611c7f565b96979596909593945091929050565b6007546000908190611584906001600160a01b03166125d5565b50509050611591816126df565b91505090565b61159f6122eb565b610e2c6000612899565b60015433906001600160a01b031681146115e15760405163118cdaa760e01b81526001600160a01b0382166004820152602401610b31565b610f9b81612899565b600554604051633e15014160e01b81526001600160a01b0383811660048301526000928392911690633e1501419060240161014060405180830381865afa158015611639573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061165d91906134f3565b505050505050509250505061271081670de0b6b3a764000061167f919061358a565b61168991906135a1565b9392505050565b600061169a6128b2565b905090565b6116a76122eb565b604080516060810182526001600160a01b0380861682528416602082015280820183905290517f5c3e76e88db066780deaf8690f603a580b3254893e1e188bd3ce4f9ae1dc4c3f906116fd9060079084906135c3565b60405180910390a18051600780546001600160a01b039283166001600160a01b03199182161790915560208301516008805491909316911617905560400151600955505050565b336117576000546001600160a01b031690565b6001600160a01b0316148061177b57503360009081526002602052604090205460ff165b6117975760405162461bcd60e51b8152600401610b3190613438565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b1580156117f257600080fd5b505af1158015611806573d6000803e3d6000fd5b505050505050565b336118216000546001600160a01b031690565b6001600160a01b0316148061184557503360009081526002602052604090205460ff165b6118615760405162461bcd60e51b8152600401610b3190613438565b61186a81612318565b61187560038261239a565b600954611880611af4565b1015610f9b5760405162461bcd60e51b8152600401610b319061347e565b6000336118b36000546001600160a01b031690565b6001600160a01b031614806118d757503360009081526002602052604090205460ff165b6118f35760405162461bcd60e51b8152600401610b3190613438565b6006546001600160a01b03166119305760405162461bcd60e51b8152602060048201526002602482015261433160f01b6044820152606401610b31565b600061193a611067565b915050806000815181106119505761195061334e565b60200260200101519150505b92915050565b61196a6122eb565b60048054600754604051635a3b74b960e01b81526001600160a01b0391821693810193909352600160248401521690635a3b74b990604401600060405180830381600087803b1580156119bc57600080fd5b505af11580156119d0573d6000803e3d6000fd5b50506007546040516001600160a01b0390911681527fb25c8f95d34cadb2b40b681c17a8ebf748aa066c29e956ad46457fac2a0cb4c6925060200190505b60405180910390a1565b60048054604051632fe4a15f60e21b8152309281019290925260009182918291829182916001600160a01b039091169063bf92857c9060240160c060405180830381865afa158015611a6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a92919061361a565b50945050509250925060006127108285611aac919061358a565b611ab691906135a1565b9050828111611ac6576000611ad0565b611ad083826134cb565b9550828110611ae0576000611aea565b611aea81846134cb565b9450505050509091565b60048054604051632fe4a15f60e21b815230928101929092526000916001600160a01b039091169063bf92857c9060240160c060405180830381865afa158015611b42573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b66919061361a565b9695505050505050565b6000806000806000806000611b83611af4565b9650611b8d611e59565b9096509450611b9a61156a565b9350611ba4612009565b9250611bae611690565b9150611bb8611c75565b905090919293949596565b611bcb6122eb565b600080856001600160a01b0316858585604051611be99291906133ab565b60006040518083038185875af1925050503d8060008114611c26576040519150601f19603f3d011682016040523d82523d6000602084013e611c2b565b606091505b5091509150818190610c405760405162461bcd60e51b8152600401610b3191906133bb565b611c586122eb565b6000611c6383612939565b9050611c7083828461295e565b505050565b600061169a612b16565b604080516002808252606082018352600092839283929091602083019080368337505060408051600280825260608201835293945060009390925090602083019080368337505060075484519293506001600160a01b031691849150600090611cea57611cea61334e565b6001600160a01b039283166020918202929092010152600854835191169083906001908110611d1b57611d1b61334e565b6001600160a01b039283166020918202929092010152600354604051634e91ecf960e11b8152911690639d23d9f290611d58908590600401613664565b600060405180830381865afa158015611d75573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611d9d9190810190613737565b905080600081518110611db257611db261334e565b602002602001015181600181518110611dcd57611dcd61334e565b60200260200101519350935050509091565b611de76122eb565b611c7083838361295e565b33611e056000546001600160a01b031690565b6001600160a01b03161480611e2957503360009081526002602052604090205460ff165b611e455760405162461bcd60e51b8152600401610b3190613438565b611e4e81612b9d565b61187560018261239a565b600080611e64611a18565b6007549193509150611e7f906001600160a01b031683612bee565b600854909250611e98906001600160a01b031682612bee565b90509091565b60048054604051632fe4a15f60e21b815230928101929092526000916001600160a01b039091169063bf92857c9060240160c060405180830381865afa158015611eec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f10919061361a565b509395945050505050565b611f236122eb565b611c70838383612439565b611f366122eb565b6001600160a01b0381166115e15760405162461bcd60e51b815260206004820152601860248201527f4954424f776e61626c653a207a65726f206164647265737300000000000000006044820152606401610b31565b60048054604051632fe4a15f60e21b815230928101929092526000916001600160a01b039091169063bf92857c9060240160c060405180830381865afa158015611fda573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ffe919061361a565b509295945050505050565b6008546000908190612023906001600160a01b03166125d5565b92505050611591816126df565b6120386122eb565b600180546001600160a01b0383166001600160a01b031990911681179091556120696000546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b6120a96122eb565b610e4282823361295e565b6120bc6122eb565b600554604080516001600160a01b03928316815291831660208301527f4a9f6afb8ebccedd27b079bb7eb7671558dabc522819eb7c603e06fbec786833910160405180910390a1600580546001600160a01b0319166001600160a01b0392909216919091179055565b6040516370a0823160e01b81526001600160a01b038281166004830152600091908416906370a0823190602401602060405180830381865afa15801561216f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116899190613774565b61219b6122eb565b60048054600a546040516328530a4760e01b815260ff909116928101929092526001600160a01b0316906328530a4790602401600060405180830381600087803b1580156121e857600080fd5b505af11580156121fc573d6000803e3d6000fd5b5050600a5460405160ff90911681527f5897b8586b1ccae5788603c723d10779309b62a3330f57991e0c52624587d50192506020019050611a0e565b6122406122eb565b600061224b82612939565b9050610e4282823361295e565b3361226b6000546001600160a01b031690565b6001600160a01b0316148061228f57503360009081526002602052604090205460ff165b6122ab5760405162461bcd60e51b8152600401610b3190613438565b60006122b5612009565b9050806000036122c25750565b6122cd60001961256e565b60085460045461105c916001600160a01b0390811691166000612439565b6000546001600160a01b03163314610e2c5760405163118cdaa760e01b8152336004820152602401610b31565b60048054600754604051631a4ca37b60e21b81526001600160a01b03918216938101939093526024830184905230604484015216906369328dec906064015b6020604051808303816000875af1158015612376573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e429190613774565b8160038111156123ac576123ac61378d565b6007546008547f5352340a216a8b13edd0af33f2cc09f5a0cb11caf74b3a53379aacc1a139890e91849133916001600160a01b0390811691166123ed61156a565b6123f5612009565b604080519687526001600160a01b0395861660208801529385169386019390935292166060840152608083019190915260a082015260c00160405180910390a25050565b8015612458576124536001600160a01b0384168383612c7b565b6124cd565b60405163095ea7b360e01b81526001600160a01b0383811660048301526024820183905284169063095ea7b3906044016020604051808303816000875af11580156124a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124cb91906137a3565b505b604080516001600160a01b038481168252602082018490528516917feded619173dbb378903f97d44ecec898a1c4876f445ae551e063113aef58b471910160405180910390a2505050565b611c7083838384612d3f565b6004805460075460405163617ba03760e01b81526001600160a01b03918216938101939093526024830184905230604484015260006064840152169063617ba03790608401610ee2565b60085460045461258b916001600160a01b03908116911683612518565b6004805460085460405163573ade8160e01b81526001600160a01b03918216938101939093526024830184905260026044840152306064840152169063573ade8190608401612357565b6005546040516334924edb60e21b81526001600160a01b038381166004830152600092839283929091169063d2493b6c90602401606060405180830381865afa158015612626573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061264a91906137be565b9196909550909350915050565b60065460405163bb492bf560e01b815260609182916001600160a01b039091169063bb492bf59061268e9086903090600401613800565b6000604051808303816000875af11580156126ad573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526126d5919081019061382a565b9094909350915050565b6040516370a0823160e01b81523060048201526000906001600160a01b038316906370a0823190602401602060405180830381865afa158015612726573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061195c9190613774565b6005546007546040516335ea6a7560e01b81526001600160a01b03918216600482015260009283928392839283928392839291909116906335ea6a759060240161018060405180830381865afa1580156127a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127cc91906138ef565b50506005546008546040516335ea6a7560e01b8152999e50979c50959a506000995089985088976001600160a01b03968716976335ea6a759761281a975016945060040192506131ff915050565b61018060405180830381865afa158015612838573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061285c91906138ef565b505050505050509450945094505050859950838561287a919061398c565b9850829750808261288b919061398c565b965050505050505090919293565b600180546001600160a01b0319169055610f9b81612dc3565b6005546007546040516335ea6a7560e01b81526001600160a01b03918216600482015260009291909116906335ea6a759060240161018060405180830381865afa158015612904573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061292891906138ef565b50949b9a5050505050505050505050565b60006001600160a01b0382161561295857612953826126df565b61195c565b4761195c565b6001600160a01b0381166129a65760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b6044820152606401610b31565b60006129b184612939565b9050828110156129f85760405162461bcd60e51b8152602060048201526012602482015271496e73756666696369656e742066756e647360701b6044820152606401610b31565b6001600160a01b038416612aaf576000826001600160a01b03168460405160006040518083038185875af1925050503d8060008114612a53576040519150601f19603f3d011682016040523d82523d6000602084013e612a58565b606091505b5050905080612aa95760405162461bcd60e51b815260206004820152601760248201527f4e6174697665207472616e73666572206661696c65642e0000000000000000006044820152606401610b31565b50612ac3565b612ac36001600160a01b0385168385612e13565b836001600160a01b0316826001600160a01b03167f9207361cc2a04b9c7a06691df1eb87c6a63957ae88bf01d0d18c81e3d127209985604051612b0891815260200190565b60405180910390a350505050565b6005546008546040516335ea6a7560e01b81526001600160a01b03918216600482015260009291909116906335ea6a759060240161018060405180830381865afa158015612b68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b8c91906138ef565b50939b9a5050505050505050505050565b6004805460085460405163a415bcad60e01b81526001600160a01b0391821693810193909352602483018490526002604484015260006064840152306084840152169063a415bcad9060a401610ee2565b60035460405163b3596f0760e01b81526001600160a01b038481166004830152600092169063b3596f0790602401602060405180830381865afa158015612c39573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c5d9190613774565b612c6684612e44565b612c7190600a613a83565b61167f908461358a565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052612ccc8482612eb1565b612d39576040516001600160a01b03848116602483015260006044830152612d2f91869182169063095ea7b3906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050612efb565b612d398482612efb565b50505050565b604051636eb1769f60e11b81523060048201526001600160a01b03848116602483015283919086169063dd62ed3e90604401602060405180830381865afa158015612d8e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612db29190613774565b1015612d3957612d39848483612439565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6040516001600160a01b03838116602483015260448201839052611c7091859182169063a9059cbb90606401612cfd565b6000816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e84573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ea89190613a8f565b60ff1692915050565b6000806000806020600086516020880160008a5af192503d91506000519050828015611b6657508115612ee75780600114611b66565b50505050506001600160a01b03163b151590565b600080602060008451602086016000885af180612f1e576040513d6000823e3d81fd5b50506000513d91508115612f36578060011415612f43565b6001600160a01b0384163b155b15612d3957604051635274afe760e01b81526001600160a01b0385166004820152602401610b31565b60008083601f840112612f7e57600080fd5b50813567ffffffffffffffff811115612f9657600080fd5b6020830191508360208260051b8501011115612fb157600080fd5b9250929050565b60008060008060008060608789031215612fd157600080fd5b863567ffffffffffffffff80821115612fe957600080fd5b612ff58a838b01612f6c565b9098509650602089013591508082111561300e57600080fd5b61301a8a838b01612f6c565b9096509450604089013591508082111561303357600080fd5b5061304089828a01612f6c565b979a9699509497509295939492505050565b6001600160a01b0381168114610f9b57600080fd5b60006020828403121561307957600080fd5b813561168981613052565b6000806040838503121561309757600080fd5b82356130a281613052565b915060208301356130b281613052565b809150509250929050565b6000602082840312156130cf57600080fd5b5035919050565b60ff81168114610f9b57600080fd5b600080600080608085870312156130fb57600080fd5b843561310681613052565b9350602085013561311681613052565b925060408501359150606085013561312d816130d6565b939692955090935050565b600081518084526020808501945080840160005b838110156131715781516001600160a01b03168752958201959082019060010161314c565b509495945050505050565b60408152600061318f6040830185613138565b82810360208481019190915284518083528582019282019060005b818110156131c6578451835293830193918301916001016131aa565b5090979650505050505050565b600080604083850312156131e657600080fd5b82356131f181613052565b946020939093013593505050565b6001600160a01b0391909116815260200190565b60008060006060848603121561322857600080fd5b833561323381613052565b9250602084013561324381613052565b929592945050506040919091013590565b6000806000806060858703121561326a57600080fd5b843561327581613052565b935060208501359250604085013567ffffffffffffffff8082111561329957600080fd5b818701915087601f8301126132ad57600080fd5b8135818111156132bc57600080fd5b8860208285010111156132ce57600080fd5b95989497505060200194505050565b81516001600160a01b03908116825260208084015190911690820152604080830151908201526060810161195c565b60008060006060848603121561332157600080fd5b833561332c81613052565b925060208401359150604084013561334381613052565b809150509250925092565b634e487b7160e01b600052603260045260246000fd5b6000808335601e1984360301811261337b57600080fd5b83018035915067ffffffffffffffff82111561339657600080fd5b602001915036819003821315612fb157600080fd5b8183823760009101908152919050565b600060208083528351808285015260005b818110156133e8578581018301518582016040015282016133cc565b506000604082860101526040601f19601f8301168501019250505092915050565b634e487b7160e01b600052601160045260246000fd5b60006001820161343157613431613409565b5060010190565b60208082526026908201527f45786563757461626c653a2063616c6c6572206973206e6f742074686520657860408201526532b1baba37b960d11b606082015260800190565b6020808252601f908201527f44616e6765726f7573204865616c746820466163746f72207265616368656400604082015260600190565b634e487b7160e01b600052604160045260246000fd5b8181038181111561195c5761195c613409565b805180151581146134ee57600080fd5b919050565b6000806000806000806000806000806101408b8d03121561351357600080fd5b8a51995060208b0151985060408b0151975060608b0151965060808b0151955061353f60a08c016134de565b945061354d60c08c016134de565b935061355b60e08c016134de565b925061356a6101008c016134de565b91506135796101208c016134de565b90509295989b9194979a5092959850565b808202811582820484141761195c5761195c613409565b6000826135be57634e487b7160e01b600052601260045260246000fd5b500490565b82546001600160a01b03908116825260018401541660208201526002830154604082015260c08101611689606083018480516001600160a01b03908116835260208083015190911690830152604090810151910152565b60008060008060008060c0878903121561363357600080fd5b865195506020870151945060408701519350606087015192506080870151915060a087015190509295509295509295565b6020815260006116896020830184613138565b604051601f8201601f1916810167ffffffffffffffff811182821017156136a0576136a06134b5565b604052919050565b600067ffffffffffffffff8211156136c2576136c26134b5565b5060051b60200190565b600082601f8301126136dd57600080fd5b815160206136f26136ed836136a8565b613677565b82815260059290921b8401810191818101908684111561371157600080fd5b8286015b8481101561372c5780518352918301918301613715565b509695505050505050565b60006020828403121561374957600080fd5b815167ffffffffffffffff81111561376057600080fd5b61376c848285016136cc565b949350505050565b60006020828403121561378657600080fd5b5051919050565b634e487b7160e01b600052602160045260246000fd5b6000602082840312156137b557600080fd5b611689826134de565b6000806000606084860312156137d357600080fd5b83516137de81613052565b60208501519093506137ef81613052565b604085015190925061334381613052565b6040815260006138136040830185613138565b905060018060a01b03831660208301529392505050565b6000806040838503121561383d57600080fd5b825167ffffffffffffffff8082111561385557600080fd5b818501915085601f83011261386957600080fd5b815160206138796136ed836136a8565b82815260059290921b8401810191818101908984111561389857600080fd5b948201945b838610156138bf5785516138b081613052565b8252948201949082019061389d565b918801519196509093505050808211156138d857600080fd5b506138e5858286016136cc565b9150509250929050565b6000806000806000806000806000806000806101808d8f03121561391257600080fd5b8c519b5060208d01519a5060408d0151995060608d0151985060808d0151975060a08d0151965060c08d0151955060e08d015194506101008d015193506101208d015192506101408d015191506101608d015164ffffffffff8116811461397857600080fd5b809150509295989b509295989b509295989b565b8082018082111561195c5761195c613409565b600181815b808511156139da5781600019048211156139c0576139c0613409565b808516156139cd57918102915b93841c93908002906139a4565b509250929050565b6000826139f15750600161195c565b816139fe5750600061195c565b8160018114613a145760028114613a1e57613a3a565b600191505061195c565b60ff841115613a2f57613a2f613409565b50506001821b61195c565b5060208310610133831016604e8410600b8410161715613a5d575081810a61195c565b613a67838361399f565b8060001904821115613a7b57613a7b613409565b029392505050565b600061168983836139e2565b600060208284031215613aa157600080fd5b8151611689816130d656fea2646970667358221220ff02f45c2832e361858501d2022e6c9a7e6d470681f82f959ab06334acdb4eb864736f6c634300081500330000000000000000000000000000000000000000000000000000000000000120000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000008236a87084f8b84306f72007f36f2618a56344940000000000000000000000006c3ea9036406852006290770bedfcaba0e23a0e80000000000000000000000000000000000000000000000000e92596fd6290000000000000000000000000000c13e21b648a5ee794902342038ff3adab66be9870000000000000000000000008105f69d9c41644c6a0803fda7d03aa70996cfd9000000000000000000000000fc21d6d146e6086b8359705c8b28512a983db0cb0000000000000000000000004370d3b6c9588e02ce9d22e684387859c7ff5b34000000000000000000000000000000000000000000000000000000000000000100000000000000000000000050b37e1cb515110be21a2c91fa3052c413e42737

Deployed Bytecode

0x60806040526004361061036f5760003560e01c80639a99b4f0116101c6578063ccbefbd5116100f7578063f2fde38b11610095578063f7888aec1161006f578063f7888aec14610a69578063f8d3a4b814610a89578063fa09e63014610a9e578063fa3ae6dc14610abe57600080fd5b8063f2fde38b14610a09578063f3fef3a314610a29578063f66c5f2a14610a4957600080fd5b8063e1b97139116100d1578063e1b97139146109a1578063e30c3978146109c1578063e38464f2146109df578063ee9af25d146109f457600080fd5b8063ccbefbd514610957578063cf12fa291461096c578063da3e33971461098157600080fd5b8063b61d27f611610164578063bd9a548b1161013e578063bd9a548b146108a4578063c3f909d4146108b9578063c4e2c1e614610917578063c5ebeaec1461093757600080fd5b8063b61d27f61461085c578063b6703fcd1461086f578063ba1c5e801461088f57600080fd5b80639c448049116101a05780639c4480491461079f578063a5f352b7146107c9578063a9f3025e146107de578063b381cf401461082857600080fd5b80639a99b4f01461072a5780639ac2a0111461074a5780639b09dd621461078a57600080fd5b80634603fd89116102a05780637b86e42c1161023e5780638da5cb5b116102185780638da5cb5b14610698578063902835f6146106ca5780639169d833146106ea5780639504ad6d1461070a57600080fd5b80637b86e42c1461063757806384bdc9a81461065757806387ba901e1461066c57600080fd5b8063578b92a61161027a578063578b92a6146105b65780636c9c2faf146105f8578063715018a61461060d57806379ba50971461062257600080fd5b80634603fd891461055357806347e7ef24146105735780634a96689a1461059357600080fd5b806334b10a6d1161030d578063371fd8e6116102e7578063371fd8e6146104db578063372500ab146104fb57806339d1fc821461051e5780633a2c08a71461053e57600080fd5b806334b10a6d1461047b578063354030231461049b578063371f30ca146104bb57600080fd5b8063247884291161034957806324788429146103d05780632a1d7543146103f05780632ec8cd8c146104465780633419ba231461045b57600080fd5b8063077d97d71461037b5780630b9b1f9a146103905780631f5a0bbe146103b057600080fd5b3661037657005b600080fd5b61038e610389366004612fb8565b610ad3565b005b34801561039c57600080fd5b5061038e6103ab366004613067565b610c49565b3480156103bc57600080fd5b5061038e6103cb366004613067565b610cba565b3480156103dc57600080fd5b5061038e6103eb366004613067565b610d25565b3480156103fc57600080fd5b5060075460085460095461041b926001600160a01b0390811692169083565b604080516001600160a01b039485168152939092166020840152908201526060015b60405180910390f35b34801561045257600080fd5b5061038e610d8d565b34801561046757600080fd5b5061038e610476366004613084565b610e2e565b34801561048757600080fd5b5061038e6104963660046130bd565b610e46565b3480156104a757600080fd5b5061038e6104b63660046130bd565b610f17565b3480156104c757600080fd5b5061038e6104d63660046130e5565b610f9e565b3480156104e757600080fd5b5061038e6104f63660046130bd565b611000565b34801561050757600080fd5b50610510611067565b60405161043d92919061317c565b34801561052a57600080fd5b5061038e610539366004613067565b61126a565b34801561054a57600080fd5b5061038e6112db565b34801561055f57600080fd5b5061038e61056e366004613067565b611399565b34801561057f57600080fd5b5061038e61058e3660046131d3565b61140a565b34801561059f57600080fd5b506105a8611466565b60405190815260200161043d565b3480156105c257600080fd5b506105cb611538565b604080519687526020870195909552938501929092526060840152608083015260a082015260c00161043d565b34801561060457600080fd5b506105a861156a565b34801561061957600080fd5b5061038e611597565b34801561062e57600080fd5b5061038e6115a9565b34801561064357600080fd5b506105a8610652366004613067565b6115ea565b34801561066357600080fd5b506105a8611690565b34801561067857600080fd5b50600a546106869060ff1681565b60405160ff909116815260200161043d565b3480156106a457600080fd5b506000546001600160a01b03165b6040516001600160a01b03909116815260200161043d565b3480156106d657600080fd5b5061038e6106e5366004613213565b61169f565b3480156106f657600080fd5b5061038e6107053660046130bd565b611744565b34801561071657600080fd5b5061038e6107253660046130bd565b61180e565b34801561073657600080fd5b506105a86107453660046131d3565b61189e565b34801561075657600080fd5b5061077a610765366004613067565b60026020526000908152604090205460ff1681565b604051901515815260200161043d565b34801561079657600080fd5b5061038e611962565b3480156107ab57600080fd5b506107b4611a18565b6040805192835260208301919091520161043d565b3480156107d557600080fd5b506105a8611af4565b3480156107ea57600080fd5b506107f3611b70565b604080519788526020880196909652948601939093526060850191909152608084015260a083015260c082015260e00161043d565b34801561083457600080fd5b506106b27f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b61038e61086a366004613254565b611bc3565b34801561087b57600080fd5b5061038e61088a366004613084565b611c50565b34801561089b57600080fd5b506105a8611c75565b3480156108b057600080fd5b506107b4611c7f565b3480156108c557600080fd5b5060408051606080820183526000808352602080840182905292840152825190810183526007546001600160a01b039081168252600854169181019190915260095481830152905161043d91906132dd565b34801561092357600080fd5b5061038e61093236600461330c565b611ddf565b34801561094357600080fd5b5061038e6109523660046130bd565b611df2565b34801561096357600080fd5b506107b4611e59565b34801561097857600080fd5b506105a8611e9e565b34801561098d57600080fd5b5061038e61099c366004613213565b611f1b565b3480156109ad57600080fd5b5061038e6109bc366004613067565b611f2e565b3480156109cd57600080fd5b506001546001600160a01b03166106b2565b3480156109eb57600080fd5b506105a8611f8c565b348015610a0057600080fd5b506105a8612009565b348015610a1557600080fd5b5061038e610a24366004613067565b612030565b348015610a3557600080fd5b5061038e610a443660046131d3565b6120a1565b348015610a5557600080fd5b5061038e610a64366004613067565b6120b4565b348015610a7557600080fd5b506105a8610a84366004613084565b612125565b348015610a9557600080fd5b5061038e612193565b348015610aaa57600080fd5b5061038e610ab9366004613067565b612238565b348015610aca57600080fd5b5061038e612258565b610adb6122eb565b8483148015610ae957508481145b610b3a5760405162461bcd60e51b815260206004820152601960248201527f417267756d656e7473206c656e677468206d69736d617463680000000000000060448201526064015b60405180910390fd5b60005b85811015610c4057600080888884818110610b5a57610b5a61334e565b9050602002016020810190610b6f9190613067565b6001600160a01b0316878785818110610b8a57610b8a61334e565b90506020020135868686818110610ba357610ba361334e565b9050602002810190610bb59190613364565b604051610bc39291906133ab565b60006040518083038185875af1925050503d8060008114610c00576040519150601f19603f3d011682016040523d82523d6000602084013e610c05565b606091505b5091509150818190610c2a5760405162461bcd60e51b8152600401610b3191906133bb565b5050508080610c389061341f565b915050610b3d565b50505050505050565b610c516122eb565b600454604080516001600160a01b03928316815291831660208301527f4f2328eba313841d245841fd4c1c80ecf36e9102ca4a5f8e9c663fd470ad08a2910160405180910390a1600480546001600160a01b0319166001600160a01b0392909216919091179055565b610cc26122eb565b604051600181526001600160a01b038216907f9fdbc2d48b8a0db2f62663bf9312ad02f5b1f6414ad600b55a247d09aeec3ea29060200160405180910390a26001600160a01b03166000908152600260205260409020805460ff19166001179055565b610d2d6122eb565b604051600081526001600160a01b038216907f9fdbc2d48b8a0db2f62663bf9312ad02f5b1f6414ad600b55a247d09aeec3ea29060200160405180910390a26001600160a01b03166000908152600260205260409020805460ff19169055565b33610da06000546001600160a01b031690565b6001600160a01b03161480610dc457503360009081526002602052604090205460ff165b610de05760405162461bcd60e51b8152600401610b3190613438565b6000610dea61156a565b9050610df7600019612318565b610e0260038261239a565b50600954610e0e611af4565b1015610e2c5760405162461bcd60e51b8152600401610b319061347e565b565b610e366122eb565b610e4282826000612439565b5050565b33610e596000546001600160a01b031690565b6001600160a01b03161480610e7d57503360009081526002602052604090205460ff165b610e995760405162461bcd60e51b8152600401610b3190613438565b604051632e1a7d4d60e01b8152600481018290527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031690632e1a7d4d906024015b600060405180830381600087803b158015610efc57600080fd5b505af1158015610f10573d6000803e3d6000fd5b5050505050565b33610f2a6000546001600160a01b031690565b6001600160a01b03161480610f4e57503360009081526002602052604090205460ff165b610f6a5760405162461bcd60e51b8152600401610b3190613438565b600754600454610f87916001600160a01b03908116911683612518565b610f9081612524565b610f9b60008261239a565b50565b610fa66122eb565b610fb184848461169f565b60405160ff821681527fb1075a43dcab7b1d3b05af43f1bba5f67e9dd2ed68c4bdfee0f83473e208448c9060200160405180910390a1600a805460ff191660ff92909216919091179055505050565b336110136000546001600160a01b031690565b6001600160a01b0316148061103757503360009081526002602052604090205460ff165b6110535760405162461bcd60e51b8152600401610b3190613438565b61105c8161256e565b610f9b60028261239a565b6060803361107d6000546001600160a01b031690565b6001600160a01b031614806110a157503360009081526002602052604090205460ff165b6110bd5760405162461bcd60e51b8152600401610b3190613438565b6006546001600160a01b03166110fa5760405162461bcd60e51b8152602060048201526002602482015261433160f01b6044820152606401610b31565b60408051600480825260a08201909252600091602082016080803683375050600754919250600091829150611137906001600160a01b03166125d5565b925050915081836000815181106111505761115061334e565b60200260200101906001600160a01b031690816001600160a01b03168152505080836001815181106111845761118461334e565b6001600160a01b03928316602091820292909201015260085460009182916111ac91166125d5565b925050915081856002815181106111c5576111c561334e565b60200260200101906001600160a01b031690816001600160a01b03168152505080856003815181106111f9576111f961334e565b60200260200101906001600160a01b031690816001600160a01b03168152505061122285612657565b60405191985096507ffaecd94ee6c58a110cb23850825a20cea2b79260d0ed1eacd9356c39618282bd90611259908990899061317c565b60405180910390a150505050509091565b6112726122eb565b600354604080516001600160a01b03928316815291831660208301527ff3920b145a63851522088bd18b14d6bb919fbd12ac87f12498d6001c727ba070910160405180910390a1600380546001600160a01b0319166001600160a01b0392909216919091179055565b6112e36122eb565b60048054600754604051635a3b74b960e01b81526001600160a01b0391821693810193909352600060248401521690635a3b74b990604401600060405180830381600087803b15801561133557600080fd5b505af1158015611349573d6000803e3d6000fd5b50506007546040516001600160a01b0390911681527fbe3bd67f54846f0ab016cb173a6b7bcdf9e06726594cf1a96faea16bf6d504809250602001905060405180910390a1600954610e0e611af4565b6113a16122eb565b600654604080516001600160a01b03928316815291831660208301527f495515279f4af7982f79035bbefde974a37f2be9a86595cdeb73385e0efe9eee910160405180910390a1600680546001600160a01b0319166001600160a01b0392909216919091179055565b3361141d6000546001600160a01b031690565b6001600160a01b0316148061144157503360009081526002602052604090205460ff165b61145d5760405162461bcd60e51b8152600401610b3190613438565b610e4281610f17565b60003361147b6000546001600160a01b031690565b6001600160a01b0316148061149f57503360009081526002602052604090205460ff165b6114bb5760405162461bcd60e51b8152600401610b3190613438565b6114c3612258565b6007546000906114db906001600160a01b03166126df565b90506114e5610d8d565b60075481906114fc906001600160a01b03166126df565b61150691906134cb565b915050600760020154611517611af4565b10156115355760405162461bcd60e51b8152600401610b319061347e565b90565b60008060008060008061154961274a565b9298509096509350915061155b611c7f565b96979596909593945091929050565b6007546000908190611584906001600160a01b03166125d5565b50509050611591816126df565b91505090565b61159f6122eb565b610e2c6000612899565b60015433906001600160a01b031681146115e15760405163118cdaa760e01b81526001600160a01b0382166004820152602401610b31565b610f9b81612899565b600554604051633e15014160e01b81526001600160a01b0383811660048301526000928392911690633e1501419060240161014060405180830381865afa158015611639573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061165d91906134f3565b505050505050509250505061271081670de0b6b3a764000061167f919061358a565b61168991906135a1565b9392505050565b600061169a6128b2565b905090565b6116a76122eb565b604080516060810182526001600160a01b0380861682528416602082015280820183905290517f5c3e76e88db066780deaf8690f603a580b3254893e1e188bd3ce4f9ae1dc4c3f906116fd9060079084906135c3565b60405180910390a18051600780546001600160a01b039283166001600160a01b03199182161790915560208301516008805491909316911617905560400151600955505050565b336117576000546001600160a01b031690565b6001600160a01b0316148061177b57503360009081526002602052604090205460ff165b6117975760405162461bcd60e51b8152600401610b3190613438565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b1580156117f257600080fd5b505af1158015611806573d6000803e3d6000fd5b505050505050565b336118216000546001600160a01b031690565b6001600160a01b0316148061184557503360009081526002602052604090205460ff165b6118615760405162461bcd60e51b8152600401610b3190613438565b61186a81612318565b61187560038261239a565b600954611880611af4565b1015610f9b5760405162461bcd60e51b8152600401610b319061347e565b6000336118b36000546001600160a01b031690565b6001600160a01b031614806118d757503360009081526002602052604090205460ff165b6118f35760405162461bcd60e51b8152600401610b3190613438565b6006546001600160a01b03166119305760405162461bcd60e51b8152602060048201526002602482015261433160f01b6044820152606401610b31565b600061193a611067565b915050806000815181106119505761195061334e565b60200260200101519150505b92915050565b61196a6122eb565b60048054600754604051635a3b74b960e01b81526001600160a01b0391821693810193909352600160248401521690635a3b74b990604401600060405180830381600087803b1580156119bc57600080fd5b505af11580156119d0573d6000803e3d6000fd5b50506007546040516001600160a01b0390911681527fb25c8f95d34cadb2b40b681c17a8ebf748aa066c29e956ad46457fac2a0cb4c6925060200190505b60405180910390a1565b60048054604051632fe4a15f60e21b8152309281019290925260009182918291829182916001600160a01b039091169063bf92857c9060240160c060405180830381865afa158015611a6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a92919061361a565b50945050509250925060006127108285611aac919061358a565b611ab691906135a1565b9050828111611ac6576000611ad0565b611ad083826134cb565b9550828110611ae0576000611aea565b611aea81846134cb565b9450505050509091565b60048054604051632fe4a15f60e21b815230928101929092526000916001600160a01b039091169063bf92857c9060240160c060405180830381865afa158015611b42573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b66919061361a565b9695505050505050565b6000806000806000806000611b83611af4565b9650611b8d611e59565b9096509450611b9a61156a565b9350611ba4612009565b9250611bae611690565b9150611bb8611c75565b905090919293949596565b611bcb6122eb565b600080856001600160a01b0316858585604051611be99291906133ab565b60006040518083038185875af1925050503d8060008114611c26576040519150601f19603f3d011682016040523d82523d6000602084013e611c2b565b606091505b5091509150818190610c405760405162461bcd60e51b8152600401610b3191906133bb565b611c586122eb565b6000611c6383612939565b9050611c7083828461295e565b505050565b600061169a612b16565b604080516002808252606082018352600092839283929091602083019080368337505060408051600280825260608201835293945060009390925090602083019080368337505060075484519293506001600160a01b031691849150600090611cea57611cea61334e565b6001600160a01b039283166020918202929092010152600854835191169083906001908110611d1b57611d1b61334e565b6001600160a01b039283166020918202929092010152600354604051634e91ecf960e11b8152911690639d23d9f290611d58908590600401613664565b600060405180830381865afa158015611d75573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611d9d9190810190613737565b905080600081518110611db257611db261334e565b602002602001015181600181518110611dcd57611dcd61334e565b60200260200101519350935050509091565b611de76122eb565b611c7083838361295e565b33611e056000546001600160a01b031690565b6001600160a01b03161480611e2957503360009081526002602052604090205460ff165b611e455760405162461bcd60e51b8152600401610b3190613438565b611e4e81612b9d565b61187560018261239a565b600080611e64611a18565b6007549193509150611e7f906001600160a01b031683612bee565b600854909250611e98906001600160a01b031682612bee565b90509091565b60048054604051632fe4a15f60e21b815230928101929092526000916001600160a01b039091169063bf92857c9060240160c060405180830381865afa158015611eec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f10919061361a565b509395945050505050565b611f236122eb565b611c70838383612439565b611f366122eb565b6001600160a01b0381166115e15760405162461bcd60e51b815260206004820152601860248201527f4954424f776e61626c653a207a65726f206164647265737300000000000000006044820152606401610b31565b60048054604051632fe4a15f60e21b815230928101929092526000916001600160a01b039091169063bf92857c9060240160c060405180830381865afa158015611fda573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ffe919061361a565b509295945050505050565b6008546000908190612023906001600160a01b03166125d5565b92505050611591816126df565b6120386122eb565b600180546001600160a01b0383166001600160a01b031990911681179091556120696000546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b6120a96122eb565b610e4282823361295e565b6120bc6122eb565b600554604080516001600160a01b03928316815291831660208301527f4a9f6afb8ebccedd27b079bb7eb7671558dabc522819eb7c603e06fbec786833910160405180910390a1600580546001600160a01b0319166001600160a01b0392909216919091179055565b6040516370a0823160e01b81526001600160a01b038281166004830152600091908416906370a0823190602401602060405180830381865afa15801561216f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116899190613774565b61219b6122eb565b60048054600a546040516328530a4760e01b815260ff909116928101929092526001600160a01b0316906328530a4790602401600060405180830381600087803b1580156121e857600080fd5b505af11580156121fc573d6000803e3d6000fd5b5050600a5460405160ff90911681527f5897b8586b1ccae5788603c723d10779309b62a3330f57991e0c52624587d50192506020019050611a0e565b6122406122eb565b600061224b82612939565b9050610e4282823361295e565b3361226b6000546001600160a01b031690565b6001600160a01b0316148061228f57503360009081526002602052604090205460ff165b6122ab5760405162461bcd60e51b8152600401610b3190613438565b60006122b5612009565b9050806000036122c25750565b6122cd60001961256e565b60085460045461105c916001600160a01b0390811691166000612439565b6000546001600160a01b03163314610e2c5760405163118cdaa760e01b8152336004820152602401610b31565b60048054600754604051631a4ca37b60e21b81526001600160a01b03918216938101939093526024830184905230604484015216906369328dec906064015b6020604051808303816000875af1158015612376573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e429190613774565b8160038111156123ac576123ac61378d565b6007546008547f5352340a216a8b13edd0af33f2cc09f5a0cb11caf74b3a53379aacc1a139890e91849133916001600160a01b0390811691166123ed61156a565b6123f5612009565b604080519687526001600160a01b0395861660208801529385169386019390935292166060840152608083019190915260a082015260c00160405180910390a25050565b8015612458576124536001600160a01b0384168383612c7b565b6124cd565b60405163095ea7b360e01b81526001600160a01b0383811660048301526024820183905284169063095ea7b3906044016020604051808303816000875af11580156124a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124cb91906137a3565b505b604080516001600160a01b038481168252602082018490528516917feded619173dbb378903f97d44ecec898a1c4876f445ae551e063113aef58b471910160405180910390a2505050565b611c7083838384612d3f565b6004805460075460405163617ba03760e01b81526001600160a01b03918216938101939093526024830184905230604484015260006064840152169063617ba03790608401610ee2565b60085460045461258b916001600160a01b03908116911683612518565b6004805460085460405163573ade8160e01b81526001600160a01b03918216938101939093526024830184905260026044840152306064840152169063573ade8190608401612357565b6005546040516334924edb60e21b81526001600160a01b038381166004830152600092839283929091169063d2493b6c90602401606060405180830381865afa158015612626573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061264a91906137be565b9196909550909350915050565b60065460405163bb492bf560e01b815260609182916001600160a01b039091169063bb492bf59061268e9086903090600401613800565b6000604051808303816000875af11580156126ad573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526126d5919081019061382a565b9094909350915050565b6040516370a0823160e01b81523060048201526000906001600160a01b038316906370a0823190602401602060405180830381865afa158015612726573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061195c9190613774565b6005546007546040516335ea6a7560e01b81526001600160a01b03918216600482015260009283928392839283928392839291909116906335ea6a759060240161018060405180830381865afa1580156127a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127cc91906138ef565b50506005546008546040516335ea6a7560e01b8152999e50979c50959a506000995089985088976001600160a01b03968716976335ea6a759761281a975016945060040192506131ff915050565b61018060405180830381865afa158015612838573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061285c91906138ef565b505050505050509450945094505050859950838561287a919061398c565b9850829750808261288b919061398c565b965050505050505090919293565b600180546001600160a01b0319169055610f9b81612dc3565b6005546007546040516335ea6a7560e01b81526001600160a01b03918216600482015260009291909116906335ea6a759060240161018060405180830381865afa158015612904573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061292891906138ef565b50949b9a5050505050505050505050565b60006001600160a01b0382161561295857612953826126df565b61195c565b4761195c565b6001600160a01b0381166129a65760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b6044820152606401610b31565b60006129b184612939565b9050828110156129f85760405162461bcd60e51b8152602060048201526012602482015271496e73756666696369656e742066756e647360701b6044820152606401610b31565b6001600160a01b038416612aaf576000826001600160a01b03168460405160006040518083038185875af1925050503d8060008114612a53576040519150601f19603f3d011682016040523d82523d6000602084013e612a58565b606091505b5050905080612aa95760405162461bcd60e51b815260206004820152601760248201527f4e6174697665207472616e73666572206661696c65642e0000000000000000006044820152606401610b31565b50612ac3565b612ac36001600160a01b0385168385612e13565b836001600160a01b0316826001600160a01b03167f9207361cc2a04b9c7a06691df1eb87c6a63957ae88bf01d0d18c81e3d127209985604051612b0891815260200190565b60405180910390a350505050565b6005546008546040516335ea6a7560e01b81526001600160a01b03918216600482015260009291909116906335ea6a759060240161018060405180830381865afa158015612b68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b8c91906138ef565b50939b9a5050505050505050505050565b6004805460085460405163a415bcad60e01b81526001600160a01b0391821693810193909352602483018490526002604484015260006064840152306084840152169063a415bcad9060a401610ee2565b60035460405163b3596f0760e01b81526001600160a01b038481166004830152600092169063b3596f0790602401602060405180830381865afa158015612c39573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c5d9190613774565b612c6684612e44565b612c7190600a613a83565b61167f908461358a565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052612ccc8482612eb1565b612d39576040516001600160a01b03848116602483015260006044830152612d2f91869182169063095ea7b3906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050612efb565b612d398482612efb565b50505050565b604051636eb1769f60e11b81523060048201526001600160a01b03848116602483015283919086169063dd62ed3e90604401602060405180830381865afa158015612d8e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612db29190613774565b1015612d3957612d39848483612439565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6040516001600160a01b03838116602483015260448201839052611c7091859182169063a9059cbb90606401612cfd565b6000816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e84573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ea89190613a8f565b60ff1692915050565b6000806000806020600086516020880160008a5af192503d91506000519050828015611b6657508115612ee75780600114611b66565b50505050506001600160a01b03163b151590565b600080602060008451602086016000885af180612f1e576040513d6000823e3d81fd5b50506000513d91508115612f36578060011415612f43565b6001600160a01b0384163b155b15612d3957604051635274afe760e01b81526001600160a01b0385166004820152602401610b31565b60008083601f840112612f7e57600080fd5b50813567ffffffffffffffff811115612f9657600080fd5b6020830191508360208260051b8501011115612fb157600080fd5b9250929050565b60008060008060008060608789031215612fd157600080fd5b863567ffffffffffffffff80821115612fe957600080fd5b612ff58a838b01612f6c565b9098509650602089013591508082111561300e57600080fd5b61301a8a838b01612f6c565b9096509450604089013591508082111561303357600080fd5b5061304089828a01612f6c565b979a9699509497509295939492505050565b6001600160a01b0381168114610f9b57600080fd5b60006020828403121561307957600080fd5b813561168981613052565b6000806040838503121561309757600080fd5b82356130a281613052565b915060208301356130b281613052565b809150509250929050565b6000602082840312156130cf57600080fd5b5035919050565b60ff81168114610f9b57600080fd5b600080600080608085870312156130fb57600080fd5b843561310681613052565b9350602085013561311681613052565b925060408501359150606085013561312d816130d6565b939692955090935050565b600081518084526020808501945080840160005b838110156131715781516001600160a01b03168752958201959082019060010161314c565b509495945050505050565b60408152600061318f6040830185613138565b82810360208481019190915284518083528582019282019060005b818110156131c6578451835293830193918301916001016131aa565b5090979650505050505050565b600080604083850312156131e657600080fd5b82356131f181613052565b946020939093013593505050565b6001600160a01b0391909116815260200190565b60008060006060848603121561322857600080fd5b833561323381613052565b9250602084013561324381613052565b929592945050506040919091013590565b6000806000806060858703121561326a57600080fd5b843561327581613052565b935060208501359250604085013567ffffffffffffffff8082111561329957600080fd5b818701915087601f8301126132ad57600080fd5b8135818111156132bc57600080fd5b8860208285010111156132ce57600080fd5b95989497505060200194505050565b81516001600160a01b03908116825260208084015190911690820152604080830151908201526060810161195c565b60008060006060848603121561332157600080fd5b833561332c81613052565b925060208401359150604084013561334381613052565b809150509250925092565b634e487b7160e01b600052603260045260246000fd5b6000808335601e1984360301811261337b57600080fd5b83018035915067ffffffffffffffff82111561339657600080fd5b602001915036819003821315612fb157600080fd5b8183823760009101908152919050565b600060208083528351808285015260005b818110156133e8578581018301518582016040015282016133cc565b506000604082860101526040601f19601f8301168501019250505092915050565b634e487b7160e01b600052601160045260246000fd5b60006001820161343157613431613409565b5060010190565b60208082526026908201527f45786563757461626c653a2063616c6c6572206973206e6f742074686520657860408201526532b1baba37b960d11b606082015260800190565b6020808252601f908201527f44616e6765726f7573204865616c746820466163746f72207265616368656400604082015260600190565b634e487b7160e01b600052604160045260246000fd5b8181038181111561195c5761195c613409565b805180151581146134ee57600080fd5b919050565b6000806000806000806000806000806101408b8d03121561351357600080fd5b8a51995060208b0151985060408b0151975060608b0151965060808b0151955061353f60a08c016134de565b945061354d60c08c016134de565b935061355b60e08c016134de565b925061356a6101008c016134de565b91506135796101208c016134de565b90509295989b9194979a5092959850565b808202811582820484141761195c5761195c613409565b6000826135be57634e487b7160e01b600052601260045260246000fd5b500490565b82546001600160a01b03908116825260018401541660208201526002830154604082015260c08101611689606083018480516001600160a01b03908116835260208083015190911690830152604090810151910152565b60008060008060008060c0878903121561363357600080fd5b865195506020870151945060408701519350606087015192506080870151915060a087015190509295509295509295565b6020815260006116896020830184613138565b604051601f8201601f1916810167ffffffffffffffff811182821017156136a0576136a06134b5565b604052919050565b600067ffffffffffffffff8211156136c2576136c26134b5565b5060051b60200190565b600082601f8301126136dd57600080fd5b815160206136f26136ed836136a8565b613677565b82815260059290921b8401810191818101908684111561371157600080fd5b8286015b8481101561372c5780518352918301918301613715565b509695505050505050565b60006020828403121561374957600080fd5b815167ffffffffffffffff81111561376057600080fd5b61376c848285016136cc565b949350505050565b60006020828403121561378657600080fd5b5051919050565b634e487b7160e01b600052602160045260246000fd5b6000602082840312156137b557600080fd5b611689826134de565b6000806000606084860312156137d357600080fd5b83516137de81613052565b60208501519093506137ef81613052565b604085015190925061334381613052565b6040815260006138136040830185613138565b905060018060a01b03831660208301529392505050565b6000806040838503121561383d57600080fd5b825167ffffffffffffffff8082111561385557600080fd5b818501915085601f83011261386957600080fd5b815160206138796136ed836136a8565b82815260059290921b8401810191818101908984111561389857600080fd5b948201945b838610156138bf5785516138b081613052565b8252948201949082019061389d565b918801519196509093505050808211156138d857600080fd5b506138e5858286016136cc565b9150509250929050565b6000806000806000806000806000806000806101808d8f03121561391257600080fd5b8c519b5060208d01519a5060408d0151995060608d0151985060808d0151975060a08d0151965060c08d0151955060e08d015194506101008d015193506101208d015192506101408d015191506101608d015164ffffffffff8116811461397857600080fd5b809150509295989b509295989b509295989b565b8082018082111561195c5761195c613409565b600181815b808511156139da5781600019048211156139c0576139c0613409565b808516156139cd57918102915b93841c93908002906139a4565b509250929050565b6000826139f15750600161195c565b816139fe5750600061195c565b8160018114613a145760028114613a1e57613a3a565b600191505061195c565b60ff841115613a2f57613a2f613409565b50506001821b61195c565b5060208310610133831016604e8410600b8410161715613a5d575081810a61195c565b613a67838361399f565b8060001904821115613a7b57613a7b613409565b029392505050565b600061168983836139e2565b600060208284031215613aa157600080fd5b8151611689816130d656fea2646970667358221220ff02f45c2832e361858501d2022e6c9a7e6d470681f82f959ab06334acdb4eb864736f6c63430008150033

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ 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.