ETH Price: $2,046.16 (-1.70%)

Contract

0x1F26fAAc7DCdBe356d21d12AEdE2C2fF3aCB044e
 

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
Adjust232480162025-08-29 16:36:23209 days ago1756485383IN
0x1F26fAAc...F3aCB044e
0 ETH0.000218683.00895285
Adjust Price230806702025-08-06 7:55:47233 days ago1754466947IN
0x1F26fAAc...F3aCB044e
0 ETH0.000029390.58958326
Adjust Price222170392025-04-07 12:31:47353 days ago1744029107IN
0x1F26fAAc...F3aCB044e
0 ETH0.000209864.00275263

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Method Block
From
To
0x6101c060220910562025-03-20 22:26:59371 days ago1742509619  Contract Creation0 ETH
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 0x489c4040...059e71bE4
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
Position

Compiler Version
v0.8.26+commit.8a97fa7a

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 14 : Position.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IMintingHubGateway} from "../gateway/interface/IMintingHubGateway.sol";
import {IDecentralizedEURO} from "../interface/IDecentralizedEURO.sol";
import {IReserve} from "../interface/IReserve.sol";
import {MathUtil} from "../utils/MathUtil.sol";
import {IMintingHub} from "./interface/IMintingHub.sol";
import {IPosition} from "./interface/IPosition.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

/**
 * @title Position
 * @notice A collateralized minting position.
 */
contract Position is Ownable, IPosition, MathUtil {
    /**
     * @notice Note that this contract is intended to be cloned. All clones will share the same values for
     * the constant and immutable fields, but have their own values for the other fields.
     */

    /**
     * @notice The deuro price per unit of the collateral below which challenges succeed, (36 - collateral.decimals) decimals
     */
    uint256 public price;

    /**
     * @notice How much has been minted in total. This variable is only used in the parent position.
     */
    uint256 private totalMinted;

    uint256 public immutable limit;

    /**
     * @notice Amount of the collateral that is currently under a challenge.
     * Used to figure out whether there are pending challenges.
     */
    uint256 public challengedAmount;

    /**
     * @notice The price at which the challenge was initiated.
     */
    uint256 private challengedPrice;

    /**
     * @notice Challenge period in seconds.
     */
    uint40 public immutable challengePeriod;

    /**
     * @notice Timestamp when minting can start and the position is no longer denied.
     */
    uint40 public immutable start;

    /**
     * @notice End of the latest cooldown. If this is in the future, minting is suspended.
     */
    uint40 public cooldown;

    /**
     * @notice Timestamp of the expiration of the position. After expiration, challenges cannot be averted
     * any more. This is also the basis for fee calculations.
     */
    uint40 public expiration;

    bool private closed;

    /**
     * @notice The original position to help identify clones.
     */
    address public immutable original;

    /**
     * @notice Pointer to the minting hub.
     */
    address public immutable hub;

    /**
     * @notice The Eurocoin contract.
     */
    IDecentralizedEURO public immutable deuro;

    /**
     * @notice The collateral token.
     */
    IERC20 public immutable override collateral;

    /**
     * @notice Minimum acceptable collateral amount to prevent dust.
     */
    uint256 public immutable override minimumCollateral;

    /**
     * @notice The interest in parts per million per year that is deducted when minting dEURO.
     */
    uint24 public immutable riskPremiumPPM;

    /**
     * @notice The locked-in rate (including riskPremiumPPM) for this position.
     */
    uint24 public fixedAnnualRatePPM;

    /**
     * @notice The reserve contribution in parts per million of the minted amount.
     */
    uint24 public immutable reserveContribution;

    /**
     * @notice The total principal borrowed.
     */
    uint256 public principal;

    /**
     * @notice The total outstanding interest.
     */
    uint256 public interest;

    /**
     * @notice The timestamp of the last interest accrual.
     */
    uint40 public lastAccrual;

    event MintingUpdate(uint256 collateral, uint256 price, uint256 principal);
    event PositionDenied(address indexed sender, string message); // emitted if closed by governance

    error InsufficientCollateral(uint256 needed, uint256 available);
    error TooLate();
    error RepaidTooMuch(uint256 excess);
    error LimitExceeded(uint256 tried, uint256 available);
    error ChallengeTooSmall();
    error Expired(uint40 time, uint40 expiration);
    error Alive();
    error Closed();
    error Hot();
    error Challenged();
    error NotHub();
    error NotOriginal();
    error InvalidExpiration();
    error AlreadyInitialized();
    error PriceTooHigh(uint256 newPrice, uint256 maxPrice);

    modifier alive() {
        if (block.timestamp >= expiration) revert Expired(uint40(block.timestamp), expiration);
        _;
    }

    // requires that the position has always been backed by a minimal amount of collateral
    modifier backed() {
        if (isClosed()) revert Closed();
        _;
    }

    modifier expired() {
        if (block.timestamp < expiration) revert Alive();
        _;
    }

    modifier noCooldown() {
        if (block.timestamp <= cooldown) revert Hot();
        _;
    }

    modifier noChallenge() {
        if (challengedAmount > 0) revert Challenged();
        _;
    }

    modifier onlyHub() {
        if (msg.sender != address(hub)) revert NotHub();
        _;
    }

    modifier ownerOrRoller() {
        if (msg.sender != address(IMintingHub(hub).ROLLER())) _checkOwner();
        _;
    }

    /**
     * @dev See MintingHub.openPosition
     *
     * @param _riskPremiumPPM ppm of minted amount that is added to the applicable minting fee as a risk premium
     */
    constructor(
        address _owner,
        address _hub,
        address _deuro,
        address _collateral,
        uint256 _minCollateral,
        uint256 _initialLimit,
        uint40 _initPeriod,
        uint40 _duration,
        uint40 _challengePeriod,
        uint24 _riskPremiumPPM,
        uint256 _liqPrice,
        uint24 _reservePPM
    ) Ownable(_owner) {
        original = address(this);
        hub = _hub;
        deuro = IDecentralizedEURO(_deuro);
        collateral = IERC20(_collateral);
        riskPremiumPPM = _riskPremiumPPM;
        reserveContribution = _reservePPM;
        minimumCollateral = _minCollateral;
        challengePeriod = _challengePeriod;
        start = uint40(block.timestamp) + _initPeriod; // at least three days time to deny the position
        cooldown = start;
        expiration = start + _duration;
        limit = _initialLimit;
        _setPrice(_liqPrice, _initialLimit);
        _fixRateToLeadrate(_riskPremiumPPM);
    }

    /**
     * Initialization method for clones.
     * Can only be called once. Should be called immediately after creating the clone.
     */
    function initialize(address parent, uint40 _expiration) external onlyHub {
        if (expiration != 0) revert AlreadyInitialized();
        if (_expiration < block.timestamp || _expiration > Position(original).expiration()) revert InvalidExpiration(); // expiration must not be later than original
        expiration = _expiration;
        price = Position(parent).price();
        _fixRateToLeadrate(Position(parent).riskPremiumPPM());
        _transferOwnership(hub);
    }

    /**
     * Cloning a position is only allowed if the position is not challenged, not expired and not in cooldown.
     */
    function assertCloneable() external noChallenge noCooldown alive backed {}

    /**
     * Notify the original that some amount has been minted.
     */
    function notifyMint(uint256 mint_) external {
        if (deuro.getPositionParent(msg.sender) != hub) revert NotHub();
        totalMinted += mint_;
    }

    function notifyRepaid(uint256 repaid_) external {
        if (deuro.getPositionParent(msg.sender) != hub) revert NotHub();
        totalMinted -= repaid_;
    }

    /**
     * Should only be called on the original position.
     * Better use 'availableForMinting'.
     */
    function availableForClones() external view returns (uint256) {
        // reserve capacity for the original to the extent the owner provided collateral
        uint256 potential = (_collateralBalance() * price) / ONE_DEC18;
        uint256 unusedPotential = principal > potential ? 0 : potential - principal;
        if (totalMinted + unusedPotential >= limit) {
            return 0;
        } else {
            return limit - totalMinted - unusedPotential;
        }
    }

    /**
     * The amount available for minting in this position family.
     *
     * Does not check if positions are challenged, closed, or under cooldown.
     */
    function availableForMinting() public view returns (uint256) {
        if (address(this) == original) {
            return limit - totalMinted;
        } else {
            return Position(original).availableForClones();
        }
    }

    /**
     * @notice Qualified pool share holders can call this method to immediately expire a freshly proposed position.
     */
    function deny(address[] calldata helpers, string calldata message) external {
        if (block.timestamp >= start) revert TooLate();
        IReserve(deuro.reserve()).checkQualified(msg.sender, helpers);
        _close();
        emit PositionDenied(msg.sender, message);
    }

    /**
     * Closes the position by putting it into eternal cooldown.
     * This allows the users to still withdraw the collateral that is left, but never to mint again.
     */
    function _close() internal {
        closed = true;
    }

    function isClosed() public view returns (bool) {
        return closed;
    }

    /**
     * @notice This is how much the minter can actually use when minting deuro, with the rest being assigned
     * to the minter reserve.
     */
    function getUsableMint(uint256 mintAmount) public view returns (uint256) {
        return (mintAmount * (1000_000 - reserveContribution)) / 1000_000;
    }

    /**
     * Returns the corresponding mint amount (disregarding the limit).
     */
    function getMintAmount(uint256 usableMint) external view returns (uint256) {
        return _ceilDivPPM(usableMint, reserveContribution);
    }

    /**
     * @notice "All in one" function to adjust the principal, the collateral amount,
     * and the price in one transaction.
     */
    function adjust(uint256 newPrincipal, uint256 newCollateral, uint256 newPrice) external onlyOwner {
        uint256 colbal = _collateralBalance();
        if (newCollateral > colbal) {
            collateral.transferFrom(msg.sender, address(this), newCollateral - colbal);
        }
        // Must be called after collateral deposit, but before withdrawal
        if (newPrincipal < principal) {
            uint256 debt = principal + _accrueInterest();
            _payDownDebt(debt - newPrincipal);
        }
        if (newCollateral < colbal) {
            _withdrawCollateral(msg.sender, colbal - newCollateral);
        }
        // Must be called after collateral withdrawal
        if (newPrincipal > principal) {
            _mint(msg.sender, newPrincipal - principal, newCollateral);
        }
        if (newPrice != price) {
            _adjustPrice(newPrice);
        }
        emit MintingUpdate(newCollateral, newPrice, newPrincipal);
    }

    /**
     * @notice Allows the position owner to adjust the liquidation price as long as there is no pending challenge.
     * Lowering the liquidation price can be done with immediate effect, given that there is enough collateral.
     * Increasing the liquidation price triggers a cooldown period of 3 days, during which minting is suspended.
     */
    function adjustPrice(uint256 newPrice) public onlyOwner {
        _adjustPrice(newPrice);
        emit MintingUpdate(_collateralBalance(), price, principal);
    }

    function _adjustPrice(uint256 newPrice) internal noChallenge alive backed noCooldown {
        if (newPrice > price) {
            _restrictMinting(3 days);
        } else {
            _checkCollateral(_collateralBalance(), newPrice);
        }
        _setPrice(newPrice, principal + availableForMinting());
    }

    function _setPrice(uint256 newPrice, uint256 bounds) internal {
        uint256 colBalance = _collateralBalance();
        if (block.timestamp >= start && newPrice > 2 * price) {
            revert PriceTooHigh(newPrice, 2 * price);
        }
        if (newPrice * colBalance > bounds * ONE_DEC18) {
            revert PriceTooHigh(newPrice, (bounds * ONE_DEC18) / colBalance);
        }
        price = newPrice;
    }

    function _collateralBalance() internal view returns (uint256) {
        return IERC20(collateral).balanceOf(address(this));
    }

    /**
     * @notice Mint deuro as long as there is no open challenge, the position is not subject to a cooldown,
     * and there is sufficient collateral.
     */
    function mint(address target, uint256 amount) public ownerOrRoller {
        uint256 collateralBalance = _collateralBalance();
        _mint(target, amount, collateralBalance);
        emit MintingUpdate(collateralBalance, price, principal);
    }

    /**
     * @notice Returns the virtual price of the collateral in dEURO.
     */
    function virtualPrice() public view returns (uint256) {
        return _virtualPrice(_collateralBalance(), price);
    }

    /**
     * @notice Computes the virtual price of the collateral in dEURO, which is the minimum collateral
     * price required to cover the entire debt with interest overcollateralization, lower bounded by the floor price. 
     * Returns the challenged price if a challenge is active.
     * @param colBalance The collateral balance of the position.
     * @param floorPrice The minimum price of the collateral in dEURO.
     */
    function _virtualPrice(uint256 colBalance, uint256 floorPrice) internal view returns (uint256) {
        if (challengedAmount > 0) return challengedPrice;
        if (colBalance == 0) return floorPrice;
        
        uint256 virtPrice = (_getCollateralRequirement() * ONE_DEC18) / colBalance;
        return virtPrice < floorPrice ? floorPrice: virtPrice;
    }

    /**
     * @notice Fixes the annual rate to the current leadrate plus the risk premium.
     * This re-prices the entire position based on the current leadrate.
     */
    function _fixRateToLeadrate(uint24 _riskPremiumPPM) internal {
        fixedAnnualRatePPM = IMintingHub(hub).RATE().currentRatePPM() + _riskPremiumPPM;
    }

    /**
     * @notice Accrues interest on the principal amount since the last accrual time.
     * @return newInterest The total outstanding interest to be paid.
     */
    function _accrueInterest() internal returns (uint256 newInterest) {
        newInterest = _calculateInterest();

        if (newInterest > interest) {
            interest = newInterest;
        }

        lastAccrual = uint40(block.timestamp);
    }

    /**
     * @notice Computes the total outstanding interest, including newly accrued interest.
     * @dev This function calculates interest accumulated since the last accrual based on
     * the principal amount, the annual interest rate, and the elapsed time.
     * The newly accrued interest is added to the current outstanding interest.
     * @return newInterest The total outstanding interest, including newly accrued interest.
     */
    function _calculateInterest() internal view returns (uint256 newInterest) {
        uint256 timestamp = block.timestamp;
        newInterest = interest;

        if (timestamp > lastAccrual && principal > 0) {
            uint256 delta = timestamp - lastAccrual;
            newInterest += (principal * fixedAnnualRatePPM * delta) / (365 days * 1_000_000);
        }

        return newInterest;
    }

    /**
     * @notice Calculates the current debt (principal + accrued interest)
     * @return Total debt without interest overcollateralization
     */
    function _getDebt() internal view returns (uint256) {
        return principal + _calculateInterest();
    }

    /**
     * @notice Calculates total debt accounting for interest overcollateralization
     * @return Total debt including overcollateralized interest
     */
    function _getCollateralRequirement() internal view returns (uint256) {
        return principal + _ceilDivPPM(_calculateInterest(), reserveContribution);
    }

    /**
     * @notice Public function to calculate current debt
     * @return The total current debt (principal + current accrued interest)
     */
    function getDebt() public view returns (uint256) {
        return _getDebt();
    }
    
    /**
     * @notice Public function to calculate current debt with overcollateralized interest
     * @return The total debt including overcollateralized interest
     */
    function getCollateralRequirement() public view returns (uint256) {
        return _getCollateralRequirement();
    }

    /**
     * @notice Public function to get the current outstanding interest
     */
    function getInterest() public view returns (uint256) {
        return _calculateInterest();
    }

    function _mint(address target, uint256 amount, uint256 collateral_) internal noChallenge noCooldown alive backed {
        if (amount > availableForMinting()) revert LimitExceeded(amount, availableForMinting());

        _accrueInterest(); // accrue interest
        _fixRateToLeadrate(riskPremiumPPM); // sync interest rate with leadrate

        Position(original).notifyMint(amount);
        deuro.mintWithReserve(target, amount, reserveContribution);

        principal += amount;
        _checkCollateral(collateral_, price);
    }

    function _restrictMinting(uint40 period) internal {
        uint40 horizon = uint40(block.timestamp) + period;
        if (horizon > cooldown) {
            cooldown = horizon;
        }
    }

    /**
     * @notice Repays a specified amount of debt from `msg.sender`, prioritizing accrued interest first and then principal.
     * @dev This method integrates the logic of paying accrued interest before principal, as introduced in the continuous
     *      interest accrual model. Any interest repaid is collected as profit, and principal repayment uses `burnFromWithReserve`.
     *
     *      Unlike previous implementations, this function delegates the actual repayment steps to `_payDownDebt`, ensuring
     *      a clean separation of logic. As a result:
     *      - Any surplus `amount` beyond what is needed to pay all outstanding interest and principal is never withdrawn
     *        from `msg.sender`’s account (no leftover handling required).
     *      - The function can be called while there are challenges, though in that scenario, collateral withdrawals remain
     *        blocked until all challenges are resolved.
     *
     *      To fully close the position (bring `debt` to 0), the amount required generally follows the formula:
     *      `debt = principal + interest`. Under normal conditions, this simplifies to:
     *      `amount = (principal * (1000000 - reservePPM)) / 1000000 + interest`.
     *
     *      For example, if `principal` is 40, `interest` is 10, and `reservePPM` is 200000, repaying 42 dEURO
     *      is required to fully close the position.
     *
     * @param amount The maximum amount of dEURO that `msg.sender` is willing to repay.
     * @return used  The actual amount of dEURO used for interest and principal repayment.
     *
     * Emits a {MintingUpdate} event.
     */
    function repay(uint256 amount) public returns (uint256) {
        uint256 used = _payDownDebt(amount);
        emit MintingUpdate(_collateralBalance(), price, principal);
        return used;
    }

    function repayFull() external returns (uint256) {
        return repay(principal + _accrueInterest());
    }

    /**
     * @notice Updates oustanding principal and notifies the original position that a portion of the total
     * minted has been repaid.
     */
    function _notifyRepaid(uint256 amount) internal {
        if (amount > principal) revert RepaidTooMuch(amount - principal);
        Position(original).notifyRepaid(amount);
        principal -= amount;
    }

    /**
     * @notice Updates outstanding interest and notifies the minting hub gateway that interest has been paid.
     */
    function _notifyInterestPaid(uint256 amount) internal {
        if (amount > interest) revert RepaidTooMuch(amount - interest);
        if (IERC165(hub).supportsInterface(type(IMintingHubGateway).interfaceId)) {
            IMintingHubGateway(hub).notifyInterestPaid(amount);
        }
        interest -= amount;
    }

    /**
     * @notice Forcefully sells some of the collateral after the position has expired, using the given buyer as the source of proceeds.
     * @dev
     * - Can only be called by the minting hub once the position is expired.
     * - Requires that there are no open challenges, ensuring that a forced sale is not used to circumvent the challenge process.
     * - The proceeds from the sale are first used to repay any accrued interest (treated as profit, collected via `collectProfits`),
     *   and then the principal (via `burnFromWithReserve`). This ensures correct accounting, where interest is always realized as profit before principal is returned.
     * - If all debt is fully repaid and there are surplus proceeds, these are transferred to the position owner.
     * - If there is a shortfall (not enough proceeds to fully repay the debt) and no remaining collateral, the system covers the loss.
     *
     * Do not allow a forced sale as long as there is an open challenge. Otherwise, a forced sale by the owner
     * himself could remove any incentive to launch challenges shortly before the expiration. (CS-ZCHF2-001)
     *
     * @param buyer         The address buying the collateral. This address provides `proceeds` in dEURO to repay the outstanding debt.
     * @param colAmount     The amount of collateral to be forcibly sold and transferred to the `buyer`.
     * @param proceeds      The amount of dEURO proceeds provided by the `buyer` to repay the outstanding debt.
     *
     * Emits a {MintingUpdate} event indicating the updated collateral balance, price, and debt after the forced sale.
     */
    function forceSale(address buyer, uint256 colAmount, uint256 proceeds) external onlyHub expired noChallenge {
        uint256 debt = principal + _accrueInterest();
        uint256 remainingCollateral = _sendCollateral(buyer, colAmount); // Send collateral to buyer

        // No debt, everything goes to owner if proceeds > 0
        if (debt == 0) {
            if (proceeds > 0) {
                deuro.transferFrom(buyer, owner(), proceeds);
            }
            emit MintingUpdate(_collateralBalance(), price, principal);
            return;
        }

        // Note: A postcondition of _repayPrincipalNet is `principal + interest > 0 => proceeds == 0` (see assert below).
        proceeds = _repayInterest(buyer, proceeds);
        proceeds = _repayPrincipalNet(buyer, proceeds);

        // If remaining collateral is 0 and `principal + interest` > 0, cover the shortfall with the system.
        if (remainingCollateral == 0 && principal + interest > 0) {
            assert(proceeds == 0);
            deuro.coverLoss(address(this), principal + interest);
            if (interest > 0) {
                deuro.collectProfits(address(this), interest);
                _notifyInterestPaid(interest);
            }
            deuro.burnWithoutReserve(principal, reserveContribution);
            _notifyRepaid(principal);
        } else if (proceeds > 0) {
            // All debt paid, leftover proceeds is profit for owner
            deuro.transferFrom(buyer, owner(), proceeds);
        }

        emit MintingUpdate(_collateralBalance(), price, principal);
    }

    /**
     * @notice Withdraw any ERC20 token that might have ended up on this address.
     * Withdrawing collateral is subject to the same restrictions as withdrawCollateral(...).
     */
    function withdraw(address token, address target, uint256 amount) external onlyOwner {
        if (token == address(collateral)) {
            withdrawCollateral(target, amount);
        } else {
            uint256 balance = _collateralBalance();
            IERC20(token).transfer(target, amount);
            require(balance == _collateralBalance()); // guard against double-entry-point tokens
        }
    }

    /**
     * @notice Withdraw collateral from the position up to the extent that it is still well collateralized afterwards.
     * Not possible as long as there is an open challenge or the contract is subject to a cooldown.
     *
     * Withdrawing collateral below the minimum collateral amount formally closes the position.
     */
    function withdrawCollateral(address target, uint256 amount) public ownerOrRoller {
        uint256 balance = _withdrawCollateral(target, amount);
        emit MintingUpdate(balance, price, principal);
    }

    function _withdrawCollateral(address target, uint256 amount) internal noCooldown noChallenge returns (uint256) {
        uint256 balance = _sendCollateral(target, amount);
        _checkCollateral(balance, price);
        return balance;
    }

    /**
     * @notice Transfer the challenged collateral to the bidder. Only callable by minting hub.
     */
    function transferChallengedCollateral(address target, uint256 amount) external onlyHub {
        uint256 newBalance = _sendCollateral(target, amount);
        emit MintingUpdate(newBalance, price, principal);
    }

    function _sendCollateral(address target, uint256 amount) internal returns (uint256) {
        // Some weird tokens fail when trying to transfer 0 amounts
        if (amount > 0) {
            IERC20(collateral).transfer(target, amount);
        }
        uint256 balance = _collateralBalance();
        if (balance < minimumCollateral) {
            _close();
        }
        return balance;
    }

    /**
     * @notice This invariant must always hold and must always be checked when any of the three
     * variables change in an adverse way. Ensures that the position overcollateralizes interest
     * by the same percentage as the reserve contribution.
     */
    function _checkCollateral(uint256 collateralReserve, uint256 atPrice) internal view {
        uint256 relevantCollateral = collateralReserve < minimumCollateral ? 0 : collateralReserve;
        uint256 collateralRequirement = _getCollateralRequirement();
        
        if (relevantCollateral * atPrice < collateralRequirement * ONE_DEC18) {
            revert InsufficientCollateral(relevantCollateral * atPrice, collateralRequirement * ONE_DEC18);
        }
    }

    /**
     * @notice Repays a specified amount of debt from `msg.sender`, prioritizing accrued interest first and then principal.
     * @return The actual amount of dEURO used for interest and principal repayment.
     */
    function _payDownDebt(uint256 amount) internal returns (uint256) {
        _accrueInterest();
        if (amount == 0) return 0;

        uint256 remaining = amount;
        remaining = _repayInterest(msg.sender, remaining); // Repay interest
        remaining = _repayPrincipal(msg.sender, remaining); // Repay principal

        return amount - remaining;
    }

    /**
     * @notice Repays a specified amount of interest from `msg.sender`.
     * @dev Assumes that _accrueInterest has been called before this function.
     * @return `amount` remaining after interest repayment.
     */
    function _repayInterest(address payer, uint256 amount) internal returns (uint256) {
        uint256 repayment = (interest > amount) ? amount : interest;
        if (repayment > 0) {
            deuro.collectProfits(payer, repayment);
            _notifyInterestPaid(repayment);
            return amount - repayment;
        }
        return amount;
    }

    /**
     * @notice Repays a specified amount of principal from `msg.sender`.
     * @param payer The address of the entity repaying the debt.
     * @param amount The repayment amount, including the reserve portion.
     * @return amount remaining after principal repayment
     */
    function _repayPrincipal(address payer, uint256 amount) internal returns (uint256) {
        uint256 repayment = (principal > amount) ? amount : principal;
        if (repayment > 0) {
            uint256 returnedReserve = deuro.burnFromWithReserve(payer, repayment, reserveContribution);
            _notifyRepaid(repayment);
            return amount - (repayment - returnedReserve);
        }
        return amount;
    }

    /**
     * @notice Repays principal from `payer` using the net repayment amount (excluding reserves).
     * To repay an exact amount including reserves, use `_repayPrincipal(address payer, uint256 amount)`.
     *
     * @param payer The address of the entity repaying the debt.
     * @param amount The repayment amount, excluding the reserve portion, i.e. the net amount.
     * @return amount remaining after principal repayment.
     */
    function _repayPrincipalNet(address payer, uint256 amount) internal returns (uint256) {
        uint256 availableReserve = deuro.calculateAssignedReserve(principal, reserveContribution);
        uint256 maxRepayment = principal - availableReserve;
        uint256 repayment = amount > maxRepayment ? maxRepayment : amount;
        if (repayment > 0) {
            uint256 freedAmount = deuro.calculateFreedAmount(repayment, reserveContribution);
            uint256 returnedReserve = deuro.burnFromWithReserve(payer, freedAmount, reserveContribution);
            assert(returnedReserve == freedAmount - repayment);
            _notifyRepaid(freedAmount);
            return amount - repayment;
        }
        return amount;
    }

    /**
     * @notice Returns the liquidation price and the durations for phase1 and phase2 of the challenge.
     * Both phases are usually of equal duration, but near expiration, phase one is adjusted such that
     * it cannot last beyond the expiration date of the position.
     */
    function challengeData() external view returns (uint256 liqPrice, uint40 phase) {
        return (_virtualPrice(_collateralBalance(), price), challengePeriod);
    }

    function notifyChallengeStarted(uint256 size, uint256 _price) external onlyHub alive {
        // Require minimum size. Collateral balance can be below minimum if it was partially challenged before.
        if (size < minimumCollateral && size < _collateralBalance()) revert ChallengeTooSmall();
        if (size == 0) revert ChallengeTooSmall();

        if (challengedAmount == 0) challengedPrice = _price;
        challengedAmount += size;
    }

    /**
     * @param size amount of collateral challenged (dec18)
     */
    function notifyChallengeAverted(uint256 size) external onlyHub {
        challengedAmount -= size;

        // Don't allow minter to close the position immediately so challenge can be repeated before
        // the owner has a chance to mint more on an undercollateralized position
        _restrictMinting(1 days);
    }

    /**
     * @notice Notifies the position that a challenge was successful.
     * Everything else is assumed to be handled by the hub.
     *
     * @param _size amount of the collateral bid for
     * @return (position owner, effective challenge size in deuro, amount of principal to repay, amount of interest to pay, reserve ppm)
     */
    function notifyChallengeSucceeded(
        uint256 _size
    ) external onlyHub returns (address, uint256, uint256, uint256, uint32) {
        _accrueInterest();

        challengedAmount -= _size;
        uint256 colBal = _collateralBalance();
        if (colBal < _size) {
            _size = colBal;
        }

        // Determine how much of the debt must be repaid based on challenged collateral
        uint256 interestToPay = (colBal == 0) ? 0 : (interest * _size) / colBal;
        uint256 principalToPay = (colBal == 0) ? 0 : (principal * _size) / colBal;
        _notifyInterestPaid(interestToPay);
        _notifyRepaid(principalToPay);

        // Give time for additional challenges before the owner can mint again.
        _restrictMinting(3 days);

        return (owner(), _size, principalToPay, interestToPay, reserveContribution);
    }
}

// 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) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @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.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.1.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

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

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IFrontendGateway {
    struct FrontendCode {
        uint256 balance;
        address owner;
    }

    event FrontendCodeRegistered(address owner, bytes32 frontendCode);
    event FrontendCodeTransferred(address from, address to, bytes32 frontendCode);
    event FrontendCodeRewardsWithdrawn(address to, uint256 amount, bytes32 frontendCode);
    event NewPositionRegistered(address position, bytes32 frontendCode);
    event RateChangesProposed(address who, uint24 nextFeeRate, uint24 nextSavingsFeeRate, uint24 nextMintingFeeRate, uint256 nextChange);
    event RateChangesExecuted(address who, uint24 nextFeeRate, uint24 nextSavingsFeeRate, uint24 nextMintingFeeRate);
    
    event InvestRewardAdded(bytes32 frontendCode, address user, uint256 amount, uint256 reward);
    event RedeemRewardAdded(bytes32 frontendCode, address user, uint256 amount, uint256 reward);
    event UnwrapAndSellRewardAdded(bytes32 frontendCode, address user, uint256 amount, uint256 reward);
    event SavingsRewardAdded(bytes32 frontendCode, address saver, uint256 interest, uint256 reward);
    event PositionRewardAdded(bytes32 frontendCode, address position, uint256 amount, uint256 reward);

    error FrontendCodeAlreadyExists();
    error NotFrontendCodeOwner();
    error NotGatewayService();
    error ProposedChangesToHigh();
    error NoOpenChanges();
    error NotDoneWaiting(uint256 minmumExecutionTime);
    error EquityTooLow();

    function invest(uint256 amount, uint256 expectedShares, bytes32 frontendCode) external returns (uint256);
    function redeem(address target, uint256 shares, uint256 expectedProceeds, bytes32 frontendCode) external returns (uint256);
    function unwrapAndSell(uint256 amount, bytes32 frontendCode) external returns (uint256);

    function updateSavingCode(address savingsOwner, bytes32 frontendCode) external;
    function updateSavingRewards(address saver, uint256 interest) external;

    function registerPosition(address position, bytes32 frontendCode) external;
    function updatePositionRewards(address position, uint256 amount) external;
    function getPositionFrontendCode(address position)view external  returns(bytes32);

    // Frontend Code Logic
    function registerFrontendCode(bytes32 frontendCode) external returns (bool);
    function transferFrontendCode(bytes32 frontendCode, address to) external returns (bool);
    function withdrawRewards(bytes32 frontendCode) external returns (uint256);
    function withdrawRewardsTo(bytes32 frontendCode, address to) external returns (uint256);

    // Governance
    function proposeChanges(uint24 newFeeRatePPM_, uint24 newSavingsFeeRatePPM_, uint24 newMintingFeeRatePPM_, address[] calldata helpers) external;
    function executeChanges() external;
}

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

import {IMintingHub} from "../../MintingHubV2/interface/IMintingHub.sol";
import {IFrontendGateway} from "./IFrontendGateway.sol";

interface IMintingHubGateway {
    function GATEWAY() external view returns (IFrontendGateway);
    function notifyInterestPaid(uint256 amount) external;
    function openPosition(address _collateralAddress, uint256 _minCollateral, uint256 _initialCollateral, uint256 _mintingMaximum, uint40 _initPeriodSeconds, uint40 _expirationSeconds, uint40 _challengeSeconds, uint24 _riskPremium, uint256 _liqPrice, uint24 _reservePPM, bytes32 _frontendCode) external returns (address);
    function clone(address owner, address parent, uint256 _initialCollateral, uint256 _initialMint, uint40 expiration, bytes32 frontendCode) external returns (address);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IReserve} from "./IReserve.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IDecentralizedEURO is IERC20 {
    function suggestMinter(
        address _minter,
        uint256 _applicationPeriod,
        uint256 _applicationFee,
        string calldata _message
    ) external;

    function registerPosition(address position) external;

    function denyMinter(address minter, address[] calldata helpers, string calldata message) external;

    function reserve() external view returns (IReserve);

    function minterReserve() external view returns (uint256);

    function calculateAssignedReserve(uint256 mintedAmount, uint32 _reservePPM) external view returns (uint256);

    function calculateFreedAmount(uint256 amountExcludingReserve, uint32 _reservePPM) external view returns (uint256);

    function equity() external view returns (uint256);

    function isMinter(address minter) external view returns (bool);

    function getPositionParent(address position) external view returns (address);

    function mint(address target, uint256 amount) external;

    function mintWithReserve(address target, uint256 amount, uint32 reservePPM) external;

    function burn(uint256 amount) external;

    function burnFrom(address target, uint256 amount) external;

    function burnWithoutReserve(uint256 amount, uint32 reservePPM) external;

    function burnFromWithReserve(
        address payer,
        uint256 targetTotalBurnAmount,
        uint32 reservePPM
    ) external returns (uint256);

    function coverLoss(address source, uint256 amount) external;

    function distributeProfits(address recipient, uint256 amount) external;

    function collectProfits(address source, uint256 _amount) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface ILeadrate {
   function currentRatePPM() external view returns (uint24);
   function currentTicks() external view returns (uint64);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

interface IReserve is IERC20 {
    function invest(uint256 amount, uint256 expected) external returns (uint256);
    function checkQualified(address sender, address[] calldata helpers) external view;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import {ILeadrate} from "../../interface/ILeadrate.sol";
import {IPosition} from "./IPosition.sol";
import {PositionRoller} from "../PositionRoller.sol";

interface IMintingHub {
    function RATE() external view returns (ILeadrate);

    function ROLLER() external view returns (PositionRoller);

    function challenge(
        address _positionAddr,
        uint256 _collateralAmount,
        uint256 minimumPrice
    ) external returns (uint256);

    function bid(uint32 _challengeNumber, uint256 size, bool postponeCollateralReturn) external;

    function returnPostponedCollateral(address collateral, address target) external;

    function buyExpiredCollateral(IPosition pos, uint256 upToAmount) external returns (uint256);

    function clone(address owner, address parent, uint256 _initialCollateral, uint256 _initialMint, uint40 expiration) external returns (address);
}

File 12 of 14 : IPosition.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

interface IPosition {
    function hub() external view returns (address);

    function collateral() external view returns (IERC20);

    function minimumCollateral() external view returns (uint256);

    function price() external view returns (uint256);

    function virtualPrice() external view returns (uint256);

    function challengedAmount() external view returns (uint256);

    function original() external view returns (address);

    function expiration() external view returns (uint40);

    function cooldown() external view returns (uint40);

    function limit() external view returns (uint256);

    function challengePeriod() external view returns (uint40);

    function start() external view returns (uint40);

    function riskPremiumPPM() external view returns (uint24);

    function reserveContribution() external view returns (uint24);

    function principal() external view returns (uint256);

    function interest() external view returns (uint256);

    function lastAccrual() external view returns (uint40);

    function initialize(address parent, uint40 _expiration) external;

    function assertCloneable() external;

    function notifyMint(uint256 mint_) external;

    function notifyRepaid(uint256 repaid_) external;

    function availableForClones() external view returns (uint256);

    function availableForMinting() external view returns (uint256);

    function deny(address[] calldata helpers, string calldata message) external;

    function getUsableMint(uint256 totalMint) external view returns (uint256);

    function getMintAmount(uint256 usableMint) external view returns (uint256);

    function adjust(uint256 newMinted, uint256 newCollateral, uint256 newPrice) external;

    function adjustPrice(uint256 newPrice) external;

    function mint(address target, uint256 amount) external;

    function getDebt() external view returns (uint256);

    function getInterest() external view returns (uint256);

    function repay(uint256 amount) external returns (uint256);

    function repayFull() external returns (uint256);

    function forceSale(address buyer, uint256 colAmount, uint256 proceeds) external;

    function withdraw(address token, address target, uint256 amount) external;

    function withdrawCollateral(address target, uint256 amount) external;

    function transferChallengedCollateral(address target, uint256 amount) external;

    function challengeData() external view returns (uint256 liqPrice, uint40 phase);

    function notifyChallengeStarted(uint256 size, uint256 _price) external;

    function notifyChallengeAverted(uint256 size) external;

    function notifyChallengeSucceeded(
        uint256 _size
    ) external returns (address, uint256, uint256, uint256, uint32);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IDecentralizedEURO} from "../interface/IDecentralizedEURO.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IMintingHubGateway} from "../gateway/interface/IMintingHubGateway.sol";
import {IMintingHub} from "./interface/IMintingHub.sol";
import {IPosition} from "./interface/IPosition.sol";
import {IReserve} from "../interface/IReserve.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

/**
 * @title PositionRoller
 *
 * Helper to roll over a debt from an old position to a new one.
 * Both positions should have the same collateral. Otherwise, it does not make much sense.
 */
contract PositionRoller {
    IDecentralizedEURO private deuro;

    error NotOwner(address pos);
    error NotPosition(address pos);
    error Log(uint256, uint256, uint256);

    event Roll(address source, uint256 collWithdraw, uint256 repay, address target, uint256 collDeposit, uint256 mint);

    constructor(address deuro_) {
        deuro = IDecentralizedEURO(deuro_);
    }

    /**
     * Convenience method to roll an old position into a new one.
     *
     * Pre-condition: an allowance for the roller to spend the collateral asset on behalf of the caller,
     * i.e., one should set collateral.approve(roller, collateral.balanceOf(sourcePosition)).
     *
     * The following is assumed:
     * - If the limit of the target position permits, the user wants to roll everything.
     * - The user does not want to add additional collateral, but excess collateral is returned.
     * - If not enough can be minted in the new position, it is acceptable for the roller to use dEURO from the msg.sender.
     */
    function rollFully(IPosition source, IPosition target) external {
        rollFullyWithExpiration(source, target, target.expiration());
    }

    /**
     * Like rollFully, but with a custom expiration date for the new position.
     */
    function rollFullyWithExpiration(IPosition source, IPosition target, uint40 expiration) public {
        require(source.collateral() == target.collateral());
        uint256 principal = source.principal();
        uint256 interest = source.getInterest();
        uint256 usableMint = source.getUsableMint(principal) + interest; // Roll interest into principal
        uint256 mintAmount = target.getMintAmount(usableMint);
        uint256 collateralToWithdraw = IERC20(source.collateral()).balanceOf(address(source));
        uint256 targetPrice = target.price();
        uint256 depositAmount = (mintAmount * 10 ** 18 + targetPrice - 1) / targetPrice; // round up
        if (depositAmount > collateralToWithdraw) {
            // If we need more collateral than available from the old position, we opt for taking
            // the missing funds from the caller instead of requiring additional collateral.
            depositAmount = collateralToWithdraw;
            mintAmount = (depositAmount * target.price()) / 10 ** 18; // round down, rest will be taken from caller
        }

        roll(source, principal + interest, collateralToWithdraw, target, mintAmount, depositAmount, expiration);
    }

    /**
     * Rolls the source position into the target position using a flash loan.
     * Both the source and the target position must recognize this roller.
     * It is the responsibility of the caller to ensure that both positions are valid contracts.
     *
     * @param source The source position, must be owned by the msg.sender.
     * @param repay The amount of principal to repay from the source position using a flash loan, freeing up some or all collateral .
     * @param collWithdraw Collateral to move from the source position to the msg.sender.
     * @param target The target position. If not owned by msg.sender or if it does not have the desired expiration,
     *               it is cloned to create a position owned by the msg.sender.
     * @param mint The amount to be minted from the target position using collateral from msg.sender.
     * @param collDeposit The amount of collateral to be sent from msg.sender to the target position.
     * @param expiration The desired expiration date for the target position.
     */
    function roll(
        IPosition source,
        uint256 repay,
        uint256 collWithdraw,
        IPosition target,
        uint256 mint,
        uint256 collDeposit,
        uint40 expiration
    ) public valid(source) valid(target) own(source) {
        deuro.mint(address(this), repay); // take a flash loan
        uint256 used = source.repay(repay);
        source.withdrawCollateral(msg.sender, collWithdraw);
        if (mint > 0) {
            IERC20 targetCollateral = IERC20(target.collateral());
            if (Ownable(address(target)).owner() != msg.sender || expiration != target.expiration()) {
                targetCollateral.transferFrom(msg.sender, address(this), collDeposit); // get the new collateral
                targetCollateral.approve(target.hub(), collDeposit); // approve the new collateral and clone:
                target = _cloneTargetPosition(target, source, collDeposit, mint, expiration);
            } else {
                // We can roll into the provided existing position.
                // We do not verify whether the target position was created by the known minting hub in order
                // to allow positions to be rolled into future versions of the minting hub.
                targetCollateral.transferFrom(msg.sender, address(target), collDeposit);
                target.mint(msg.sender, mint);
            }
        }

        // Transfer remaining flash loan to caller for repayment
        if (repay > used) {
            deuro.transfer(msg.sender, repay - used);
        }

        deuro.burnFrom(msg.sender, repay); // repay the flash loan
        emit Roll(address(source), collWithdraw, repay, address(target), collDeposit, mint);
    }

    /**
     * Clones the target position and mints the specified amount using the given collateral.
     */
    function _cloneTargetPosition (
        IPosition target,
        IPosition source,
        uint256 collDeposit,
        uint256 mint,
        uint40 expiration
    ) internal returns (IPosition) {
        if (IERC165(target.hub()).supportsInterface(type(IMintingHubGateway).interfaceId)) {
            bytes32 frontendCode = IMintingHubGateway(target.hub()).GATEWAY().getPositionFrontendCode(
                address(source)
            );
            return IPosition(
                IMintingHubGateway(target.hub()).clone(
                    msg.sender,
                    address(target),
                    collDeposit,
                    mint,
                    expiration,
                    frontendCode // use the same frontend code
                )
            );
        } else {
            return IPosition(
                IMintingHub(target.hub()).clone(msg.sender, address(target), collDeposit, mint, expiration)
            );
        }
    }

    modifier own(IPosition pos) {
        if (Ownable(address(pos)).owner() != msg.sender) revert NotOwner(address(pos));
        _;
    }

    modifier valid(IPosition pos) {
        if (deuro.getPositionParent(address(pos)) == address(0x0)) revert NotPosition(address(pos));
        _;
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @title Functions for share valuation
 */
contract MathUtil {
    uint256 internal constant ONE_DEC18 = 10 ** 18;

    // Let's go for 12 digits of precision (18-6)
    uint256 internal constant THRESH_DEC18 = 10 ** 6;

    /**
     * @notice Fifth root with Halley approximation
     *         Number 1e18 decimal
     * @param _v     number for which we calculate x**(1/5)
     * @return returns _v**(1/5)
     */
    function _fifthRoot(uint256 _v) internal pure returns (uint256) {
        // Good first guess for _v slightly above 1.0, which is often the case in the dEURO system
        uint256 x = _v > ONE_DEC18 && _v < 10 ** 19 ? (_v - ONE_DEC18) / 5 + ONE_DEC18 : ONE_DEC18;
        uint256 diff;
        do {
            uint256 powX5 = _power5(x);
            uint256 xnew = (x * (2 * powX5 + 3 * _v)) / (3 * powX5 + 2 * _v);
            diff = xnew > x ? xnew - x : x - xnew;
            x = xnew;
        } while (diff > THRESH_DEC18);
        return x;
    }

    function _mulD18(uint256 _a, uint256 _b) internal pure returns (uint256) {
        return (_a * _b) / ONE_DEC18;
    }

    function _divD18(uint256 _a, uint256 _b) internal pure returns (uint256) {
        return (_a * ONE_DEC18) / _b;
    }

    function _power5(uint256 _x) internal pure returns (uint256) {
        return _mulD18(_mulD18(_mulD18(_mulD18(_x, _x), _x), _x), _x);
    }

    function _min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }
    
    /**
     * @notice Performs ceiling division for PPM calculations using formula: ceil(amount / (1 - ppm/1000000))
     * @param amount The base amount to divide
     * @param ppm Parts per million value (e.g., 200000 for 20%)
     * @return The result of ceiling division
     */
    function _ceilDivPPM(uint256 amount, uint24 ppm) internal pure returns (uint256) {
        return amount == 0 ? 0 : (amount * 1_000_000 - 1) / (1_000_000 - ppm) + 1;
    }
}

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

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_hub","type":"address"},{"internalType":"address","name":"_deuro","type":"address"},{"internalType":"address","name":"_collateral","type":"address"},{"internalType":"uint256","name":"_minCollateral","type":"uint256"},{"internalType":"uint256","name":"_initialLimit","type":"uint256"},{"internalType":"uint40","name":"_initPeriod","type":"uint40"},{"internalType":"uint40","name":"_duration","type":"uint40"},{"internalType":"uint40","name":"_challengePeriod","type":"uint40"},{"internalType":"uint24","name":"_riskPremiumPPM","type":"uint24"},{"internalType":"uint256","name":"_liqPrice","type":"uint256"},{"internalType":"uint24","name":"_reservePPM","type":"uint24"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"Alive","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"ChallengeTooSmall","type":"error"},{"inputs":[],"name":"Challenged","type":"error"},{"inputs":[],"name":"Closed","type":"error"},{"inputs":[{"internalType":"uint40","name":"time","type":"uint40"},{"internalType":"uint40","name":"expiration","type":"uint40"}],"name":"Expired","type":"error"},{"inputs":[],"name":"Hot","type":"error"},{"inputs":[{"internalType":"uint256","name":"needed","type":"uint256"},{"internalType":"uint256","name":"available","type":"uint256"}],"name":"InsufficientCollateral","type":"error"},{"inputs":[],"name":"InvalidExpiration","type":"error"},{"inputs":[{"internalType":"uint256","name":"tried","type":"uint256"},{"internalType":"uint256","name":"available","type":"uint256"}],"name":"LimitExceeded","type":"error"},{"inputs":[],"name":"NotHub","type":"error"},{"inputs":[],"name":"NotOriginal","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"uint256","name":"newPrice","type":"uint256"},{"internalType":"uint256","name":"maxPrice","type":"uint256"}],"name":"PriceTooHigh","type":"error"},{"inputs":[{"internalType":"uint256","name":"excess","type":"uint256"}],"name":"RepaidTooMuch","type":"error"},{"inputs":[],"name":"TooLate","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"collateral","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"principal","type":"uint256"}],"name":"MintingUpdate","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":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"string","name":"message","type":"string"}],"name":"PositionDenied","type":"event"},{"inputs":[{"internalType":"uint256","name":"newPrincipal","type":"uint256"},{"internalType":"uint256","name":"newCollateral","type":"uint256"},{"internalType":"uint256","name":"newPrice","type":"uint256"}],"name":"adjust","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newPrice","type":"uint256"}],"name":"adjustPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"assertCloneable","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"availableForClones","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"availableForMinting","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"challengeData","outputs":[{"internalType":"uint256","name":"liqPrice","type":"uint256"},{"internalType":"uint40","name":"phase","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"challengePeriod","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"challengedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collateral","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cooldown","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"helpers","type":"address[]"},{"internalType":"string","name":"message","type":"string"}],"name":"deny","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"deuro","outputs":[{"internalType":"contract IDecentralizedEURO","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"expiration","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fixedAnnualRatePPM","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"buyer","type":"address"},{"internalType":"uint256","name":"colAmount","type":"uint256"},{"internalType":"uint256","name":"proceeds","type":"uint256"}],"name":"forceSale","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getCollateralRequirement","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getInterest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"usableMint","type":"uint256"}],"name":"getMintAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"mintAmount","type":"uint256"}],"name":"getUsableMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hub","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"parent","type":"address"},{"internalType":"uint40","name":"_expiration","type":"uint40"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"interest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isClosed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastAccrual","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"limit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minimumCollateral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"size","type":"uint256"}],"name":"notifyChallengeAverted","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"size","type":"uint256"},{"internalType":"uint256","name":"_price","type":"uint256"}],"name":"notifyChallengeStarted","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_size","type":"uint256"}],"name":"notifyChallengeSucceeded","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"mint_","type":"uint256"}],"name":"notifyMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"repaid_","type":"uint256"}],"name":"notifyRepaid","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"original","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"price","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"principal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"repay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"repayFull","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reserveContribution","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"riskPremiumPPM","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"start","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferChallengedCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"virtualPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"}]

0x6101c060405234801561001157600080fd5b50604051613a59380380613a59833981016040819052610030916103d8565b8b6001600160a01b03811661006057604051631e4fbdf760e01b8152600060048201526024015b60405180910390fd5b6100698161012f565b503060e0526001600160a01b038b8116610100528a81166101205289166101405262ffffff8084166101805281166101a05261016088905264ffffffffff841660a0526100b686426104c2565b64ffffffffff1660c08190526005805464ffffffffff1916821790556100dd9086906104c2565b6005805464ffffffffff92909216650100000000000264ffffffffff60281b199092169190911790556080879052610115828861017f565b61011e8361022e565b505050505050505050505050610591565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000610189610320565b905060c05164ffffffffff1642101580156101b057506001546101ad9060026104e5565b83115b156101e7578260015460026101c591906104e5565b6040516387ebe85d60e01b815260048101929092526024820152604401610057565b6101f9670de0b6b3a7640000836104e5565b61020382856104e5565b111561022757828161021d670de0b6b3a7640000856104e5565b6101c591906104fc565b5050600155565b80610100516001600160a01b031663664e97046040518163ffffffff1660e01b8152600401602060405180830381865afa158015610270573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610294919061051e565b6001600160a01b03166306a7b3766040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102d1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102f59190610542565b6102ff919061055d565b6005600b6101000a81548162ffffff021916908362ffffff16021790555050565b610140516040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa15801561036a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061038e9190610578565b905090565b6001600160a01b03811681146103a857600080fd5b50565b805164ffffffffff811681146103c057600080fd5b919050565b805162ffffff811681146103c057600080fd5b6000806000806000806000806000806000806101808d8f0312156103fb57600080fd5b8c5161040681610393565b60208e0151909c5061041781610393565b60408e0151909b5061042881610393565b60608e0151909a5061043981610393565b60808e015160a08f0151919a509850965061045660c08e016103ab565b955061046460e08e016103ab565b94506104736101008e016103ab565b93506104826101208e016103c5565b6101408e0151909350915061049a6101608e016103c5565b90509295989b509295989b509295989b565b634e487b7160e01b600052601160045260246000fd5b64ffffffffff81811683821601908111156104df576104df6104ac565b92915050565b80820281158282048414176104df576104df6104ac565b60008261051957634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561053057600080fd5b815161053b81610393565b9392505050565b60006020828403121561055457600080fd5b61053b826103c5565b62ffffff81811683821601908111156104df576104df6104ac565b60006020828403121561058a57600080fd5b5051919050565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516132be61079b600039600081816103b101528181610d85015281816118450152818161188601528181611abc01528181611d1b01528181611fc4015281816120aa0152818161217e015281816127550152612c6a0152600081816104bb015261266f0152600081816105f601528181611a1401528181611e010152612b650152600081816106900152818161156a01528181611beb01528181611d840152611e9401526000818161054f0152818161084801528181610aeb01528181610c3101528181610cf801528181610dac01528181610e280152818161123d01528181611b3b01528181611f2701528181611ff8015281816120d40152818161214d015281816127220152612c3901526000818161035f0152818161099301528181610a2401528181610fa8015281816110a50152818161119701528181611219015281816112f60152818161151c015281816117450152818161198701528181611b17015281816122900152818161231e015261281801526000818161042d01528181610e9301528181610ee801528181611374015281816123c201526126a901526000818161061d015281816108010152612d0b0152600081816106fb0152610f810152600081816105ab01528181610ec201528181611901015261194201526132be6000f3fe608060405234801561001057600080fd5b50600436106102a05760003560e01c8063787a08a611610167578063babe7c74116100ce578063d8dfeb4511610087578063d8dfeb451461068b578063d9caed12146106b2578063e5a4bed3146106c5578063f2fde38b146106cd578063f37c2ecf146106e0578063f3f480d9146106f657600080fd5b8063babe7c74146105f1578063be9a655514610618578063c2b6b58c1461063f578063c392f7661461065c578063c54a89ca14610665578063cfb636f01461067857600080fd5b80639de2f796116101205780639de2f7961461058a578063a035b1fe1461059d578063a4d66daf146105a6578063a8a3e31d146105cd578063b8e2426c146105d5578063ba5d3078146105e857600080fd5b8063787a08a6146104dd5780637b3baab4146104ee5780637ccd4f71146104ff57806382b8eaf51461054a5780638da5cb5b146105715780638ea875f31461058257600080fd5b8063371fd8e61161020b5780635be980f5116101c45780635be980f5146104625780636d4749fa146104755780636f871cec14610488578063715018a61461049b57806372bf079e146104a3578063730d48b7146104b657600080fd5b8063371fd8e6146103995780633a7c29fb146103ac57806340c10f19146103e75780634665096d146103fa57806346c715fa1461042857806349746f101461044f57600080fd5b806324e657fe1161025d57806324e657fe1461030b57806329d93d67146103145780632a37eeae1461031c578063329864aa14610324578063350c35e914610347578063365a86fc1461035a57600080fd5b80630e49d77e146102a557806313a06782146102af57806314a6bf0f146102ca5780631bbea34c146102d25780631dbcff26146102e5578063211d7983146102f8575b600080fd5b6102ad61071d565b005b6102b76107e6565b6040519081526020015b60405180910390f35b6102b76107f5565b6102ad6102e0366004612e13565b6107ff565b6102ad6102f3366004612ecb565b610988565b6102ad610306366004612ef7565b610a19565b6102b760035481565b6102b7610e6d565b6102b7610e87565b61032c610f68565b6040805192835264ffffffffff9091166020830152016102c1565b6102ad610355366004612ecb565b610fa6565b6103817f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016102c1565b6102b76103a7366004612f2c565b611054565b6103d37f000000000000000000000000000000000000000000000000000000000000000081565b60405162ffffff90911681526020016102c1565b6102ad6103f5366004612ecb565b6110a3565b60055461041290600160281b900464ffffffffff1681565b60405164ffffffffff90911681526020016102c1565b6103817f000000000000000000000000000000000000000000000000000000000000000081565b6102ad61045d366004612f2c565b61118c565b6102ad610470366004612f2c565b6111fc565b6102ad610483366004612f58565b6112eb565b6102ad610496366004612f91565b611544565b6102ad6116d3565b6102ad6104b1366004612f2c565b6116e5565b6103d37f000000000000000000000000000000000000000000000000000000000000000081565b6005546104129064ffffffffff1681565b6008546104129064ffffffffff1681565b61051261050d366004612f2c565b611734565b604080516001600160a01b039096168652602086019490945292840191909152606083015263ffffffff16608082015260a0016102c1565b6103817f000000000000000000000000000000000000000000000000000000000000000081565b6000546001600160a01b0316610381565b6102b7611871565b6102b7610598366004612f2c565b61187e565b6102b760015481565b6102b77f000000000000000000000000000000000000000000000000000000000000000081565b6102b76118b0565b6102ad6105e3366004612fbd565b61197c565b6102b760065481565b6102b77f000000000000000000000000000000000000000000000000000000000000000081565b6104127f000000000000000000000000000000000000000000000000000000000000000081565b600554600160501b900460ff1660405190151581526020016102c1565b6102b760075481565b6102b7610673366004612f2c565b611ab1565b6102ad610686366004612f2c565b611afa565b6103817f000000000000000000000000000000000000000000000000000000000000000081565b6102ad6106c0366004612fdf565b611be1565b6102b7611cc7565b6102ad6106db366004613020565b611cd1565b6005546103d390600160581b900462ffffff1681565b6104127f000000000000000000000000000000000000000000000000000000000000000081565b6003541561073e5760405163d26e2de960e01b815260040160405180910390fd5b60055464ffffffffff16421161076757604051631c02820f60e21b815260040160405180910390fd5b600554600160281b900464ffffffffff1642106107b9576005546040516238ddd760e41b815264ffffffffff4281166004830152600160281b9092049190911660248201526044015b60405180910390fd5b600554600160501b900460ff16156107e457604051631cdde67b60e01b815260040160405180910390fd5b565b60006107f0611d0c565b905090565b60006107f0611d4c565b7f000000000000000000000000000000000000000000000000000000000000000064ffffffffff1642106108465760405163ecdd1c2960e01b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663cd3293de6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108a4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108c8919061303d565b6001600160a01b031663352e3a833386866040518463ffffffff1660e01b81526004016108f79392919061305a565b60006040518083038186803b15801561090f57600080fd5b505afa158015610923573d6000803e3d6000fd5b5050505061093f6005805460ff60501b1916600160501b179055565b336001600160a01b03167faca80c800ec0d2aa9d9d31b7f886a1dd3067d4676abc637626a18ffb9381653d838360405161097a9291906130b8565b60405180910390a250505050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146109d1576040516313bd2e8360e31b815260040160405180910390fd5b60006109dd8383611d56565b600154600654604080518481526020810193909352820152909150600080516020613269833981519152906060015b60405180910390a1505050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610a62576040516313bd2e8360e31b815260040160405180910390fd5b600554600160281b900464ffffffffff16421015610a93576040516302de694d60e41b815260040160405180910390fd5b60035415610ab45760405163d26e2de960e01b815260040160405180910390fd5b6000610abe611e46565b600654610acb91906130fd565b90506000610ad98585611d56565b905081600003610be7578215610ba5577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166323b872dd86610b2b6000546001600160a01b031690565b6040516001600160e01b031960e085901b1681526001600160a01b03928316600482015291166024820152604481018690526064016020604051808303816000875af1158015610b7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ba39190613110565b505b600080516020613269833981519152610bbc611e7c565b6001546006546040805193845260208401929092529082015260600160405180910390a15050505050565b610bf18584611ee3565b9250610bfd8584611fa6565b925080158015610c1c57506000600754600654610c1a91906130fd565b115b15610e20578215610c2f57610c2f613132565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d1fa5e9830600754600654610c6f91906130fd565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087803b158015610cb557600080fd5b505af1158015610cc9573d6000803e3d6000fd5b5050505060006007541115610d675760075460405163375edc7760e11b815230600482015260248101919091527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690636ebdb8ee90604401600060405180830381600087803b158015610d4457600080fd5b505af1158015610d58573d6000803e3d6000fd5b50505050610d67600754612241565b6006546040516363b20c3360e11b8152600481019190915262ffffff7f00000000000000000000000000000000000000000000000000000000000000001660248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063c764186690604401600060405180830381600087803b158015610df857600080fd5b505af1158015610e0c573d6000803e3d6000fd5b50505050610e1b600654612395565b610ba5565b8215610ba5577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166323b872dd86610b2b6000546001600160a01b031690565b505050565b60006107f0610e7a611e46565b6006546103a791906130fd565b60006001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163003610ee6576002546107f0907f0000000000000000000000000000000000000000000000000000000000000000613148565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a8a3e31d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f44573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107f0919061315b565b600080610f7e610f76611e7c565b600154612438565b927f000000000000000000000000000000000000000000000000000000000000000092509050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f09e9e3a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611004573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611028919061303d565b6001600160a01b0316336001600160a01b03161461104857611048612493565b60006109dd83836124c0565b60008061106083612527565b9050600080516020613269833981519152611079611e7c565b6001546006546040805193845260208401929092529082015260600160405180910390a192915050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f09e9e3a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611101573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611125919061303d565b6001600160a01b0316336001600160a01b03161461114557611145612493565b600061114f611e7c565b905061115c838383612565565b60015460065460408051848152602081019390935282015260008051602061326983398151915290606001610a0c565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146111d5576040516313bd2e8360e31b815260040160405180910390fd5b80600360008282546111e79190613148565b909155506111f99050620151806127d4565b50565b60405163aa5dd7f160e01b81523360048201526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116917f00000000000000000000000000000000000000000000000000000000000000009091169063aa5dd7f190602401602060405180830381865afa158015611286573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112aa919061303d565b6001600160a01b0316146112d1576040516313bd2e8360e31b815260040160405180910390fd5b80600260008282546112e39190613148565b909155505050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611334576040516313bd2e8360e31b815260040160405180910390fd5b600554600160281b900464ffffffffff16156113625760405162dc149f60e41b815260040160405180910390fd5b428164ffffffffff16108061140557507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316634665096d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113f49190613174565b64ffffffffff168164ffffffffff16115b1561142357604051637d9533a960e11b815260040160405180910390fd5b806005806101000a81548164ffffffffff021916908364ffffffffff160217905550816001600160a01b031663a035b1fe6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611483573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114a7919061315b565b600181905550611517826001600160a01b031663730d48b76040518163ffffffff1660e01b8152600401602060405180830381865afa1580156114ee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115129190613191565b612815565b6115407f0000000000000000000000000000000000000000000000000000000000000000612924565b5050565b61154c612493565b6000611556611e7c565b905080831115611615576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166323b872dd333061159b8588613148565b6040516001600160e01b031960e086901b1681526001600160a01b03938416600482015292909116602483015260448201526064016020604051808303816000875af11580156115ef573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116139190613110565b505b60065484101561164d576000611629611e46565b60065461163691906130fd565b905061164a6116458683613148565b612527565b50505b8083101561166a57611668336116638584613148565b6124c0565b505b60065484111561168c5761168c33600654866116869190613148565b85612565565b600154821461169e5761169e82612974565b60408051848152602081018490529081018590526000805160206132698339815191529060600160405180910390a150505050565b6116db612493565b6107e46000612924565b6116ed612493565b6116f681612974565b60008051602061326983398151915261170d611e7c565b6001546006546040805193845260208401929092529082015260600160405180910390a150565b600080808080336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611783576040516313bd2e8360e31b815260040160405180910390fd5b61178b611e46565b50856003600082825461179e9190613148565b90915550600090506117ae611e7c565b9050868110156117bc578096505b600081156117e25781886007546117d391906131b6565b6117dd91906131cd565b6117e5565b60005b90506000821561180d5782896006546117fe91906131b6565b61180891906131cd565b611810565b60005b905061181b82612241565b61182481612395565b6118306203f4806127d4565b6000546001600160a01b0316999097509095507f000000000000000000000000000000000000000000000000000000000000000062ffffff16945092505050565b60006107f0610f76611e7c565b60006118aa827f0000000000000000000000000000000000000000000000000000000000000000612a80565b92915050565b600080670de0b6b3a76400006001546118c7611e7c565b6118d191906131b6565b6118db91906131cd565b9050600081600654116118fa576006546118f59083613148565b6118fd565b60005b90507f00000000000000000000000000000000000000000000000000000000000000008160025461192e91906130fd565b1061193c5760009250505090565b806002547f000000000000000000000000000000000000000000000000000000000000000061196b9190613148565b6119759190613148565b9250505090565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146119c5576040516313bd2e8360e31b815260040160405180910390fd5b600554600160281b900464ffffffffff164210611a12576005546040516238ddd760e41b815264ffffffffff4281166004830152600160281b9092049190911660248201526044016107b0565b7f000000000000000000000000000000000000000000000000000000000000000082108015611a475750611a44611e7c565b82105b15611a6557604051633c80636b60e21b815260040160405180910390fd5b81600003611a8657604051633c80636b60e21b815260040160405180910390fd5b600354600003611a965760048190555b8160036000828254611aa891906130fd565b90915550505050565b6000620f4240611ae17f0000000000000000000000000000000000000000000000000000000000000000826131ef565b611af09062ffffff16846131b6565b6118aa91906131cd565b60405163aa5dd7f160e01b81523360048201526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116917f00000000000000000000000000000000000000000000000000000000000000009091169063aa5dd7f190602401602060405180830381865afa158015611b84573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ba8919061303d565b6001600160a01b031614611bcf576040516313bd2e8360e31b815260040160405180910390fd5b80600260008282546112e391906130fd565b611be9612493565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b031603611c2c57610e688282610fa6565b6000611c36611e7c565b60405163a9059cbb60e01b81526001600160a01b038581166004830152602482018590529192509085169063a9059cbb906044016020604051808303816000875af1158015611c89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cad9190613110565b50611cb6611e7c565b8114611cc157600080fd5b50505050565b60006107f0612ad6565b611cd9612493565b6001600160a01b038116611d0357604051631e4fbdf760e01b8152600060048201526024016107b0565b6111f981612924565b6000611d3f611d19612ad6565b7f0000000000000000000000000000000000000000000000000000000000000000612a80565b6006546107f091906130fd565b6000611d3f612ad6565b60008115611df35760405163a9059cbb60e01b81526001600160a01b038481166004830152602482018490527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303816000875af1158015611dcd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611df19190613110565b505b6000611dfd611e7c565b90507f0000000000000000000000000000000000000000000000000000000000000000811015611e3f57611e3f6005805460ff60501b1916600160501b179055565b9392505050565b6000611e50612ad6565b9050600754811115611e625760078190555b6008805464ffffffffff19164264ffffffffff1617905590565b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015610f44573d6000803e3d6000fd5b6000808260075411611ef757600754611ef9565b825b90508015611f9e5760405163375edc7760e11b81526001600160a01b038581166004830152602482018390527f00000000000000000000000000000000000000000000000000000000000000001690636ebdb8ee90604401600060405180830381600087803b158015611f6b57600080fd5b505af1158015611f7f573d6000803e3d6000fd5b50505050611f8c81612241565b611f968184613148565b9150506118aa565b509092915050565b6006546040516302dc1ca760e31b8152600481019190915262ffffff7f000000000000000000000000000000000000000000000000000000000000000016602482015260009081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906316e0e53890604401602060405180830381865afa15801561203f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612063919061315b565b90506000816006546120759190613148565b905060008185116120865784612088565b815b9050801561223757604051633824f22960e21b81526004810182905262ffffff7f00000000000000000000000000000000000000000000000000000000000000001660248201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063e093c8a490604401602060405180830381865afa158015612123573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612147919061315b565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663315f3e7289847f00000000000000000000000000000000000000000000000000000000000000006040518463ffffffff1660e01b81526004016121bb9392919061320a565b6020604051808303816000875af11580156121da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121fe919061315b565b905061220a8383613148565b811461221857612218613132565b61222182612395565b61222b8388613148565b955050505050506118aa565b5092949350505050565b600754811115612274576007546122589082613148565b604051635795d46960e11b81526004016107b091815260200190565b6040516301ffc9a760e01b8152632464e41b60e21b60048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906301ffc9a790602401602060405180830381865afa1580156122df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123039190613110565b1561238357604051637d0ea02d60e01b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690637d0ea02d90602401600060405180830381600087803b15801561236a57600080fd5b505af115801561237e573d6000803e3d6000fd5b505050505b80600760008282546112e39190613148565b6006548111156123ac576006546122589082613148565b604051635be980f560e01b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690635be980f590602401600060405180830381600087803b15801561240e57600080fd5b505af1158015612422573d6000803e3d6000fd5b5050505080600660008282546112e39190613148565b6003546000901561244c57506004546118aa565b8260000361245b5750806118aa565b600083670de0b6b3a764000061246f611d0c565b61247991906131b6565b61248391906131cd565b9050828110611f9e579392505050565b6000546001600160a01b031633146107e45760405163118cdaa760e01b81523360048201526024016107b0565b60055460009064ffffffffff1642116124ec57604051631c02820f60e21b815260040160405180910390fd5b6003541561250d5760405163d26e2de960e01b815260040160405180910390fd5b60006125198484611d56565b9050611e3f81600154612b61565b6000612531611e46565b508160000361254257506000919050565b8161254d3382611ee3565b90506125593382612c01565b9050611e3f8184613148565b600354156125865760405163d26e2de960e01b815260040160405180910390fd5b60055464ffffffffff1642116125af57604051631c02820f60e21b815260040160405180910390fd5b600554600160281b900464ffffffffff1642106125fc576005546040516238ddd760e41b815264ffffffffff4281166004830152600160281b9092049190911660248201526044016107b0565b600554600160501b900460ff161561262757604051631cdde67b60e01b815260040160405180910390fd5b61262f610e87565b821115612661578161263f610e87565b604051634779077960e11b8152600481019290925260248201526044016107b0565b612669611e46565b506126937f0000000000000000000000000000000000000000000000000000000000000000612815565b604051630cfb636f60e41b8152600481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063cfb636f090602401600060405180830381600087803b1580156126f557600080fd5b505af1158015612709573d6000803e3d6000fd5b505060405163165203b760e31b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016925063b2901db8915061277d90869086907f00000000000000000000000000000000000000000000000000000000000000009060040161320a565b600060405180830381600087803b15801561279757600080fd5b505af11580156127ab573d6000803e3d6000fd5b5050505081600660008282546127c191906130fd565b92505081905550610e6881600154612b61565b60006127e08242613230565b60055490915064ffffffffff9081169082161115611540576005805464ffffffffff831664ffffffffff199091161790555050565b807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663664e97046040518163ffffffff1660e01b8152600401602060405180830381865afa158015612874573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612898919061303d565b6001600160a01b03166306a7b3766040518163ffffffff1660e01b8152600401602060405180830381865afa1580156128d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128f99190613191565b612903919061324d565b6005600b6101000a81548162ffffff021916908362ffffff16021790555050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600354156129955760405163d26e2de960e01b815260040160405180910390fd5b600554600160281b900464ffffffffff1642106129e2576005546040516238ddd760e41b815264ffffffffff4281166004830152600160281b9092049190911660248201526044016107b0565b600554600160501b900460ff1615612a0d57604051631cdde67b60e01b815260040160405180910390fd5b60055464ffffffffff164211612a3657604051631c02820f60e21b815260040160405180910390fd5b600154811115612a5157612a4c6203f4806127d4565b612a62565b612a62612a5c611e7c565b82612b61565b6111f981612a6e610e87565b600654612a7b91906130fd565b612cfd565b60008215612acd57612a9582620f42406131ef565b62ffffff166001612aa985620f42406131b6565b612ab39190613148565b612abd91906131cd565b612ac89060016130fd565b611e3f565b60009392505050565b600754600854429064ffffffffff1681118015612af557506000600654115b15612b5d57600854600090612b119064ffffffffff1683613148565b9050651cae8c13e000816005600b9054906101000a900462ffffff1662ffffff16600654612b3f91906131b6565b612b4991906131b6565b612b5391906131cd565b61197590846130fd565b5090565b60007f00000000000000000000000000000000000000000000000000000000000000008310612b905782612b93565b60005b90506000612b9f611d0c565b9050612bb3670de0b6b3a7640000826131b6565b612bbd84846131b6565b1015611cc157612bcd83836131b6565b612bdf670de0b6b3a7640000836131b6565b604051632c1f8ef160e21b8152600481019290925260248201526044016107b0565b6000808260065411612c1557600654612c17565b825b90508015611f9e576040516318af9f3960e11b81526000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063315f3e7290612c9290889086907f00000000000000000000000000000000000000000000000000000000000000009060040161320a565b6020604051808303816000875af1158015612cb1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cd5919061315b565b9050612ce082612395565b612cea8183613148565b612cf49085613148565b925050506118aa565b6000612d07611e7c565b90507f000000000000000000000000000000000000000000000000000000000000000064ffffffffff164210158015612d4c5750600154612d499060026131b6565b83115b15612d8357826001546002612d6191906131b6565b6040516387ebe85d60e01b8152600481019290925260248201526044016107b0565b612d95670de0b6b3a7640000836131b6565b612d9f82856131b6565b1115612dc3578281612db9670de0b6b3a7640000856131b6565b612d6191906131cd565b5050600155565b60008083601f840112612ddc57600080fd5b50813567ffffffffffffffff811115612df457600080fd5b602083019150836020828501011115612e0c57600080fd5b9250929050565b60008060008060408587031215612e2957600080fd5b843567ffffffffffffffff811115612e4057600080fd5b8501601f81018713612e5157600080fd5b803567ffffffffffffffff811115612e6857600080fd5b8760208260051b8401011115612e7d57600080fd5b60209182019550935085013567ffffffffffffffff811115612e9e57600080fd5b612eaa87828801612dca565b95989497509550505050565b6001600160a01b03811681146111f957600080fd5b60008060408385031215612ede57600080fd5b8235612ee981612eb6565b946020939093013593505050565b600080600060608486031215612f0c57600080fd5b8335612f1781612eb6565b95602085013595506040909401359392505050565b600060208284031215612f3e57600080fd5b5035919050565b64ffffffffff811681146111f957600080fd5b60008060408385031215612f6b57600080fd5b8235612f7681612eb6565b91506020830135612f8681612f45565b809150509250929050565b600080600060608486031215612fa657600080fd5b505081359360208301359350604090920135919050565b60008060408385031215612fd057600080fd5b50508035926020909101359150565b600080600060608486031215612ff457600080fd5b8335612fff81612eb6565b9250602084013561300f81612eb6565b929592945050506040919091013590565b60006020828403121561303257600080fd5b8135611e3f81612eb6565b60006020828403121561304f57600080fd5b8151611e3f81612eb6565b6001600160a01b0384168152604060208201819052810182905260008360608301825b858110156130ad57823561309081612eb6565b6001600160a01b031682526020928301929091019060010161307d565b509695505050505050565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b634e487b7160e01b600052601160045260246000fd5b808201808211156118aa576118aa6130e7565b60006020828403121561312257600080fd5b81518015158114611e3f57600080fd5b634e487b7160e01b600052600160045260246000fd5b818103818111156118aa576118aa6130e7565b60006020828403121561316d57600080fd5b5051919050565b60006020828403121561318657600080fd5b8151611e3f81612f45565b6000602082840312156131a357600080fd5b815162ffffff81168114611e3f57600080fd5b80820281158282048414176118aa576118aa6130e7565b6000826131ea57634e487b7160e01b600052601260045260246000fd5b500490565b62ffffff82811682821603908111156118aa576118aa6130e7565b6001600160a01b03939093168352602083019190915262ffffff16604082015260600190565b64ffffffffff81811683821601908111156118aa576118aa6130e7565b62ffffff81811683821601908111156118aa576118aa6130e756fe9483a26ad376f30b5199a79e75df3bb05158c4ee32a348f53e83245a5e50c86ea26469706673582212209a1975e04fb13fc1ca52e414c56b5c239b9ac598c02c1ac429d1dfc9e289acd464736f6c634300081a003300000000000000000000000001ae4c18c2677f97bab536c48d6c36858f5c86d70000000000000000000000008b3c41c649b9c7085c171cbb82337889b3604618000000000000000000000000ba3f535bbcccca2a154b573ca6c5a49baae0a3ea000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000003782dace9d900000000000000000000000000000000000000000000000002a5a058fc295ed000000000000000000000000000000000000000000000000000000000000000003f4800000000000000000000000000000000000000000000000000000000000ed4e00000000000000000000000000000000000000000000000000000000000002a300000000000000000000000000000000000000000000000000000000000000c350000000000000000000000000000000000000000000000043c33c1937564800000000000000000000000000000000000000000000000000000000000000030d40

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106102a05760003560e01c8063787a08a611610167578063babe7c74116100ce578063d8dfeb4511610087578063d8dfeb451461068b578063d9caed12146106b2578063e5a4bed3146106c5578063f2fde38b146106cd578063f37c2ecf146106e0578063f3f480d9146106f657600080fd5b8063babe7c74146105f1578063be9a655514610618578063c2b6b58c1461063f578063c392f7661461065c578063c54a89ca14610665578063cfb636f01461067857600080fd5b80639de2f796116101205780639de2f7961461058a578063a035b1fe1461059d578063a4d66daf146105a6578063a8a3e31d146105cd578063b8e2426c146105d5578063ba5d3078146105e857600080fd5b8063787a08a6146104dd5780637b3baab4146104ee5780637ccd4f71146104ff57806382b8eaf51461054a5780638da5cb5b146105715780638ea875f31461058257600080fd5b8063371fd8e61161020b5780635be980f5116101c45780635be980f5146104625780636d4749fa146104755780636f871cec14610488578063715018a61461049b57806372bf079e146104a3578063730d48b7146104b657600080fd5b8063371fd8e6146103995780633a7c29fb146103ac57806340c10f19146103e75780634665096d146103fa57806346c715fa1461042857806349746f101461044f57600080fd5b806324e657fe1161025d57806324e657fe1461030b57806329d93d67146103145780632a37eeae1461031c578063329864aa14610324578063350c35e914610347578063365a86fc1461035a57600080fd5b80630e49d77e146102a557806313a06782146102af57806314a6bf0f146102ca5780631bbea34c146102d25780631dbcff26146102e5578063211d7983146102f8575b600080fd5b6102ad61071d565b005b6102b76107e6565b6040519081526020015b60405180910390f35b6102b76107f5565b6102ad6102e0366004612e13565b6107ff565b6102ad6102f3366004612ecb565b610988565b6102ad610306366004612ef7565b610a19565b6102b760035481565b6102b7610e6d565b6102b7610e87565b61032c610f68565b6040805192835264ffffffffff9091166020830152016102c1565b6102ad610355366004612ecb565b610fa6565b6103817f0000000000000000000000008b3c41c649b9c7085c171cbb82337889b360461881565b6040516001600160a01b0390911681526020016102c1565b6102b76103a7366004612f2c565b611054565b6103d37f0000000000000000000000000000000000000000000000000000000000030d4081565b60405162ffffff90911681526020016102c1565b6102ad6103f5366004612ecb565b6110a3565b60055461041290600160281b900464ffffffffff1681565b60405164ffffffffff90911681526020016102c1565b6103817f0000000000000000000000001f26faac7dcdbe356d21d12aede2c2ff3acb044e81565b6102ad61045d366004612f2c565b61118c565b6102ad610470366004612f2c565b6111fc565b6102ad610483366004612f58565b6112eb565b6102ad610496366004612f91565b611544565b6102ad6116d3565b6102ad6104b1366004612f2c565b6116e5565b6103d37f000000000000000000000000000000000000000000000000000000000000c35081565b6005546104129064ffffffffff1681565b6008546104129064ffffffffff1681565b61051261050d366004612f2c565b611734565b604080516001600160a01b039096168652602086019490945292840191909152606083015263ffffffff16608082015260a0016102c1565b6103817f000000000000000000000000ba3f535bbcccca2a154b573ca6c5a49baae0a3ea81565b6000546001600160a01b0316610381565b6102b7611871565b6102b7610598366004612f2c565b61187e565b6102b760015481565b6102b77f000000000000000000000000000000000000000000002a5a058fc295ed00000081565b6102b76118b0565b6102ad6105e3366004612fbd565b61197c565b6102b760065481565b6102b77f0000000000000000000000000000000000000000000000003782dace9d90000081565b6104127f0000000000000000000000000000000000000000000000000000000067e08ab381565b600554600160501b900460ff1660405190151581526020016102c1565b6102b760075481565b6102b7610673366004612f2c565b611ab1565b6102ad610686366004612f2c565b611afa565b6103817f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b6102ad6106c0366004612fdf565b611be1565b6102b7611cc7565b6102ad6106db366004613020565b611cd1565b6005546103d390600160581b900462ffffff1681565b6104127f000000000000000000000000000000000000000000000000000000000002a30081565b6003541561073e5760405163d26e2de960e01b815260040160405180910390fd5b60055464ffffffffff16421161076757604051631c02820f60e21b815260040160405180910390fd5b600554600160281b900464ffffffffff1642106107b9576005546040516238ddd760e41b815264ffffffffff4281166004830152600160281b9092049190911660248201526044015b60405180910390fd5b600554600160501b900460ff16156107e457604051631cdde67b60e01b815260040160405180910390fd5b565b60006107f0611d0c565b905090565b60006107f0611d4c565b7f0000000000000000000000000000000000000000000000000000000067e08ab364ffffffffff1642106108465760405163ecdd1c2960e01b815260040160405180910390fd5b7f000000000000000000000000ba3f535bbcccca2a154b573ca6c5a49baae0a3ea6001600160a01b031663cd3293de6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108a4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108c8919061303d565b6001600160a01b031663352e3a833386866040518463ffffffff1660e01b81526004016108f79392919061305a565b60006040518083038186803b15801561090f57600080fd5b505afa158015610923573d6000803e3d6000fd5b5050505061093f6005805460ff60501b1916600160501b179055565b336001600160a01b03167faca80c800ec0d2aa9d9d31b7f886a1dd3067d4676abc637626a18ffb9381653d838360405161097a9291906130b8565b60405180910390a250505050565b336001600160a01b037f0000000000000000000000008b3c41c649b9c7085c171cbb82337889b360461816146109d1576040516313bd2e8360e31b815260040160405180910390fd5b60006109dd8383611d56565b600154600654604080518481526020810193909352820152909150600080516020613269833981519152906060015b60405180910390a1505050565b336001600160a01b037f0000000000000000000000008b3c41c649b9c7085c171cbb82337889b36046181614610a62576040516313bd2e8360e31b815260040160405180910390fd5b600554600160281b900464ffffffffff16421015610a93576040516302de694d60e41b815260040160405180910390fd5b60035415610ab45760405163d26e2de960e01b815260040160405180910390fd5b6000610abe611e46565b600654610acb91906130fd565b90506000610ad98585611d56565b905081600003610be7578215610ba5577f000000000000000000000000ba3f535bbcccca2a154b573ca6c5a49baae0a3ea6001600160a01b03166323b872dd86610b2b6000546001600160a01b031690565b6040516001600160e01b031960e085901b1681526001600160a01b03928316600482015291166024820152604481018690526064016020604051808303816000875af1158015610b7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ba39190613110565b505b600080516020613269833981519152610bbc611e7c565b6001546006546040805193845260208401929092529082015260600160405180910390a15050505050565b610bf18584611ee3565b9250610bfd8584611fa6565b925080158015610c1c57506000600754600654610c1a91906130fd565b115b15610e20578215610c2f57610c2f613132565b7f000000000000000000000000ba3f535bbcccca2a154b573ca6c5a49baae0a3ea6001600160a01b031663d1fa5e9830600754600654610c6f91906130fd565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087803b158015610cb557600080fd5b505af1158015610cc9573d6000803e3d6000fd5b5050505060006007541115610d675760075460405163375edc7760e11b815230600482015260248101919091527f000000000000000000000000ba3f535bbcccca2a154b573ca6c5a49baae0a3ea6001600160a01b031690636ebdb8ee90604401600060405180830381600087803b158015610d4457600080fd5b505af1158015610d58573d6000803e3d6000fd5b50505050610d67600754612241565b6006546040516363b20c3360e11b8152600481019190915262ffffff7f0000000000000000000000000000000000000000000000000000000000030d401660248201527f000000000000000000000000ba3f535bbcccca2a154b573ca6c5a49baae0a3ea6001600160a01b03169063c764186690604401600060405180830381600087803b158015610df857600080fd5b505af1158015610e0c573d6000803e3d6000fd5b50505050610e1b600654612395565b610ba5565b8215610ba5577f000000000000000000000000ba3f535bbcccca2a154b573ca6c5a49baae0a3ea6001600160a01b03166323b872dd86610b2b6000546001600160a01b031690565b505050565b60006107f0610e7a611e46565b6006546103a791906130fd565b60006001600160a01b037f0000000000000000000000001f26faac7dcdbe356d21d12aede2c2ff3acb044e163003610ee6576002546107f0907f000000000000000000000000000000000000000000002a5a058fc295ed000000613148565b7f0000000000000000000000001f26faac7dcdbe356d21d12aede2c2ff3acb044e6001600160a01b031663a8a3e31d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f44573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107f0919061315b565b600080610f7e610f76611e7c565b600154612438565b927f000000000000000000000000000000000000000000000000000000000002a30092509050565b7f0000000000000000000000008b3c41c649b9c7085c171cbb82337889b36046186001600160a01b031663f09e9e3a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611004573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611028919061303d565b6001600160a01b0316336001600160a01b03161461104857611048612493565b60006109dd83836124c0565b60008061106083612527565b9050600080516020613269833981519152611079611e7c565b6001546006546040805193845260208401929092529082015260600160405180910390a192915050565b7f0000000000000000000000008b3c41c649b9c7085c171cbb82337889b36046186001600160a01b031663f09e9e3a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611101573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611125919061303d565b6001600160a01b0316336001600160a01b03161461114557611145612493565b600061114f611e7c565b905061115c838383612565565b60015460065460408051848152602081019390935282015260008051602061326983398151915290606001610a0c565b336001600160a01b037f0000000000000000000000008b3c41c649b9c7085c171cbb82337889b360461816146111d5576040516313bd2e8360e31b815260040160405180910390fd5b80600360008282546111e79190613148565b909155506111f99050620151806127d4565b50565b60405163aa5dd7f160e01b81523360048201526001600160a01b037f0000000000000000000000008b3c41c649b9c7085c171cbb82337889b36046188116917f000000000000000000000000ba3f535bbcccca2a154b573ca6c5a49baae0a3ea9091169063aa5dd7f190602401602060405180830381865afa158015611286573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112aa919061303d565b6001600160a01b0316146112d1576040516313bd2e8360e31b815260040160405180910390fd5b80600260008282546112e39190613148565b909155505050565b336001600160a01b037f0000000000000000000000008b3c41c649b9c7085c171cbb82337889b36046181614611334576040516313bd2e8360e31b815260040160405180910390fd5b600554600160281b900464ffffffffff16156113625760405162dc149f60e41b815260040160405180910390fd5b428164ffffffffff16108061140557507f0000000000000000000000001f26faac7dcdbe356d21d12aede2c2ff3acb044e6001600160a01b0316634665096d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113f49190613174565b64ffffffffff168164ffffffffff16115b1561142357604051637d9533a960e11b815260040160405180910390fd5b806005806101000a81548164ffffffffff021916908364ffffffffff160217905550816001600160a01b031663a035b1fe6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611483573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114a7919061315b565b600181905550611517826001600160a01b031663730d48b76040518163ffffffff1660e01b8152600401602060405180830381865afa1580156114ee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115129190613191565b612815565b6115407f0000000000000000000000008b3c41c649b9c7085c171cbb82337889b3604618612924565b5050565b61154c612493565b6000611556611e7c565b905080831115611615576001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2166323b872dd333061159b8588613148565b6040516001600160e01b031960e086901b1681526001600160a01b03938416600482015292909116602483015260448201526064016020604051808303816000875af11580156115ef573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116139190613110565b505b60065484101561164d576000611629611e46565b60065461163691906130fd565b905061164a6116458683613148565b612527565b50505b8083101561166a57611668336116638584613148565b6124c0565b505b60065484111561168c5761168c33600654866116869190613148565b85612565565b600154821461169e5761169e82612974565b60408051848152602081018490529081018590526000805160206132698339815191529060600160405180910390a150505050565b6116db612493565b6107e46000612924565b6116ed612493565b6116f681612974565b60008051602061326983398151915261170d611e7c565b6001546006546040805193845260208401929092529082015260600160405180910390a150565b600080808080336001600160a01b037f0000000000000000000000008b3c41c649b9c7085c171cbb82337889b36046181614611783576040516313bd2e8360e31b815260040160405180910390fd5b61178b611e46565b50856003600082825461179e9190613148565b90915550600090506117ae611e7c565b9050868110156117bc578096505b600081156117e25781886007546117d391906131b6565b6117dd91906131cd565b6117e5565b60005b90506000821561180d5782896006546117fe91906131b6565b61180891906131cd565b611810565b60005b905061181b82612241565b61182481612395565b6118306203f4806127d4565b6000546001600160a01b0316999097509095507f0000000000000000000000000000000000000000000000000000000000030d4062ffffff16945092505050565b60006107f0610f76611e7c565b60006118aa827f0000000000000000000000000000000000000000000000000000000000030d40612a80565b92915050565b600080670de0b6b3a76400006001546118c7611e7c565b6118d191906131b6565b6118db91906131cd565b9050600081600654116118fa576006546118f59083613148565b6118fd565b60005b90507f000000000000000000000000000000000000000000002a5a058fc295ed0000008160025461192e91906130fd565b1061193c5760009250505090565b806002547f000000000000000000000000000000000000000000002a5a058fc295ed00000061196b9190613148565b6119759190613148565b9250505090565b336001600160a01b037f0000000000000000000000008b3c41c649b9c7085c171cbb82337889b360461816146119c5576040516313bd2e8360e31b815260040160405180910390fd5b600554600160281b900464ffffffffff164210611a12576005546040516238ddd760e41b815264ffffffffff4281166004830152600160281b9092049190911660248201526044016107b0565b7f0000000000000000000000000000000000000000000000003782dace9d90000082108015611a475750611a44611e7c565b82105b15611a6557604051633c80636b60e21b815260040160405180910390fd5b81600003611a8657604051633c80636b60e21b815260040160405180910390fd5b600354600003611a965760048190555b8160036000828254611aa891906130fd565b90915550505050565b6000620f4240611ae17f0000000000000000000000000000000000000000000000000000000000030d40826131ef565b611af09062ffffff16846131b6565b6118aa91906131cd565b60405163aa5dd7f160e01b81523360048201526001600160a01b037f0000000000000000000000008b3c41c649b9c7085c171cbb82337889b36046188116917f000000000000000000000000ba3f535bbcccca2a154b573ca6c5a49baae0a3ea9091169063aa5dd7f190602401602060405180830381865afa158015611b84573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ba8919061303d565b6001600160a01b031614611bcf576040516313bd2e8360e31b815260040160405180910390fd5b80600260008282546112e391906130fd565b611be9612493565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316836001600160a01b031603611c2c57610e688282610fa6565b6000611c36611e7c565b60405163a9059cbb60e01b81526001600160a01b038581166004830152602482018590529192509085169063a9059cbb906044016020604051808303816000875af1158015611c89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cad9190613110565b50611cb6611e7c565b8114611cc157600080fd5b50505050565b60006107f0612ad6565b611cd9612493565b6001600160a01b038116611d0357604051631e4fbdf760e01b8152600060048201526024016107b0565b6111f981612924565b6000611d3f611d19612ad6565b7f0000000000000000000000000000000000000000000000000000000000030d40612a80565b6006546107f091906130fd565b6000611d3f612ad6565b60008115611df35760405163a9059cbb60e01b81526001600160a01b038481166004830152602482018490527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2169063a9059cbb906044016020604051808303816000875af1158015611dcd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611df19190613110565b505b6000611dfd611e7c565b90507f0000000000000000000000000000000000000000000000003782dace9d900000811015611e3f57611e3f6005805460ff60501b1916600160501b179055565b9392505050565b6000611e50612ad6565b9050600754811115611e625760078190555b6008805464ffffffffff19164264ffffffffff1617905590565b6040516370a0823160e01b81523060048201526000907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316906370a0823190602401602060405180830381865afa158015610f44573d6000803e3d6000fd5b6000808260075411611ef757600754611ef9565b825b90508015611f9e5760405163375edc7760e11b81526001600160a01b038581166004830152602482018390527f000000000000000000000000ba3f535bbcccca2a154b573ca6c5a49baae0a3ea1690636ebdb8ee90604401600060405180830381600087803b158015611f6b57600080fd5b505af1158015611f7f573d6000803e3d6000fd5b50505050611f8c81612241565b611f968184613148565b9150506118aa565b509092915050565b6006546040516302dc1ca760e31b8152600481019190915262ffffff7f0000000000000000000000000000000000000000000000000000000000030d4016602482015260009081906001600160a01b037f000000000000000000000000ba3f535bbcccca2a154b573ca6c5a49baae0a3ea16906316e0e53890604401602060405180830381865afa15801561203f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612063919061315b565b90506000816006546120759190613148565b905060008185116120865784612088565b815b9050801561223757604051633824f22960e21b81526004810182905262ffffff7f0000000000000000000000000000000000000000000000000000000000030d401660248201526000907f000000000000000000000000ba3f535bbcccca2a154b573ca6c5a49baae0a3ea6001600160a01b03169063e093c8a490604401602060405180830381865afa158015612123573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612147919061315b565b905060007f000000000000000000000000ba3f535bbcccca2a154b573ca6c5a49baae0a3ea6001600160a01b031663315f3e7289847f0000000000000000000000000000000000000000000000000000000000030d406040518463ffffffff1660e01b81526004016121bb9392919061320a565b6020604051808303816000875af11580156121da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121fe919061315b565b905061220a8383613148565b811461221857612218613132565b61222182612395565b61222b8388613148565b955050505050506118aa565b5092949350505050565b600754811115612274576007546122589082613148565b604051635795d46960e11b81526004016107b091815260200190565b6040516301ffc9a760e01b8152632464e41b60e21b60048201527f0000000000000000000000008b3c41c649b9c7085c171cbb82337889b36046186001600160a01b0316906301ffc9a790602401602060405180830381865afa1580156122df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123039190613110565b1561238357604051637d0ea02d60e01b8152600481018290527f0000000000000000000000008b3c41c649b9c7085c171cbb82337889b36046186001600160a01b031690637d0ea02d90602401600060405180830381600087803b15801561236a57600080fd5b505af115801561237e573d6000803e3d6000fd5b505050505b80600760008282546112e39190613148565b6006548111156123ac576006546122589082613148565b604051635be980f560e01b8152600481018290527f0000000000000000000000001f26faac7dcdbe356d21d12aede2c2ff3acb044e6001600160a01b031690635be980f590602401600060405180830381600087803b15801561240e57600080fd5b505af1158015612422573d6000803e3d6000fd5b5050505080600660008282546112e39190613148565b6003546000901561244c57506004546118aa565b8260000361245b5750806118aa565b600083670de0b6b3a764000061246f611d0c565b61247991906131b6565b61248391906131cd565b9050828110611f9e579392505050565b6000546001600160a01b031633146107e45760405163118cdaa760e01b81523360048201526024016107b0565b60055460009064ffffffffff1642116124ec57604051631c02820f60e21b815260040160405180910390fd5b6003541561250d5760405163d26e2de960e01b815260040160405180910390fd5b60006125198484611d56565b9050611e3f81600154612b61565b6000612531611e46565b508160000361254257506000919050565b8161254d3382611ee3565b90506125593382612c01565b9050611e3f8184613148565b600354156125865760405163d26e2de960e01b815260040160405180910390fd5b60055464ffffffffff1642116125af57604051631c02820f60e21b815260040160405180910390fd5b600554600160281b900464ffffffffff1642106125fc576005546040516238ddd760e41b815264ffffffffff4281166004830152600160281b9092049190911660248201526044016107b0565b600554600160501b900460ff161561262757604051631cdde67b60e01b815260040160405180910390fd5b61262f610e87565b821115612661578161263f610e87565b604051634779077960e11b8152600481019290925260248201526044016107b0565b612669611e46565b506126937f000000000000000000000000000000000000000000000000000000000000c350612815565b604051630cfb636f60e41b8152600481018390527f0000000000000000000000001f26faac7dcdbe356d21d12aede2c2ff3acb044e6001600160a01b03169063cfb636f090602401600060405180830381600087803b1580156126f557600080fd5b505af1158015612709573d6000803e3d6000fd5b505060405163165203b760e31b81526001600160a01b037f000000000000000000000000ba3f535bbcccca2a154b573ca6c5a49baae0a3ea16925063b2901db8915061277d90869086907f0000000000000000000000000000000000000000000000000000000000030d409060040161320a565b600060405180830381600087803b15801561279757600080fd5b505af11580156127ab573d6000803e3d6000fd5b5050505081600660008282546127c191906130fd565b92505081905550610e6881600154612b61565b60006127e08242613230565b60055490915064ffffffffff9081169082161115611540576005805464ffffffffff831664ffffffffff199091161790555050565b807f0000000000000000000000008b3c41c649b9c7085c171cbb82337889b36046186001600160a01b031663664e97046040518163ffffffff1660e01b8152600401602060405180830381865afa158015612874573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612898919061303d565b6001600160a01b03166306a7b3766040518163ffffffff1660e01b8152600401602060405180830381865afa1580156128d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128f99190613191565b612903919061324d565b6005600b6101000a81548162ffffff021916908362ffffff16021790555050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600354156129955760405163d26e2de960e01b815260040160405180910390fd5b600554600160281b900464ffffffffff1642106129e2576005546040516238ddd760e41b815264ffffffffff4281166004830152600160281b9092049190911660248201526044016107b0565b600554600160501b900460ff1615612a0d57604051631cdde67b60e01b815260040160405180910390fd5b60055464ffffffffff164211612a3657604051631c02820f60e21b815260040160405180910390fd5b600154811115612a5157612a4c6203f4806127d4565b612a62565b612a62612a5c611e7c565b82612b61565b6111f981612a6e610e87565b600654612a7b91906130fd565b612cfd565b60008215612acd57612a9582620f42406131ef565b62ffffff166001612aa985620f42406131b6565b612ab39190613148565b612abd91906131cd565b612ac89060016130fd565b611e3f565b60009392505050565b600754600854429064ffffffffff1681118015612af557506000600654115b15612b5d57600854600090612b119064ffffffffff1683613148565b9050651cae8c13e000816005600b9054906101000a900462ffffff1662ffffff16600654612b3f91906131b6565b612b4991906131b6565b612b5391906131cd565b61197590846130fd565b5090565b60007f0000000000000000000000000000000000000000000000003782dace9d9000008310612b905782612b93565b60005b90506000612b9f611d0c565b9050612bb3670de0b6b3a7640000826131b6565b612bbd84846131b6565b1015611cc157612bcd83836131b6565b612bdf670de0b6b3a7640000836131b6565b604051632c1f8ef160e21b8152600481019290925260248201526044016107b0565b6000808260065411612c1557600654612c17565b825b90508015611f9e576040516318af9f3960e11b81526000906001600160a01b037f000000000000000000000000ba3f535bbcccca2a154b573ca6c5a49baae0a3ea169063315f3e7290612c9290889086907f0000000000000000000000000000000000000000000000000000000000030d409060040161320a565b6020604051808303816000875af1158015612cb1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cd5919061315b565b9050612ce082612395565b612cea8183613148565b612cf49085613148565b925050506118aa565b6000612d07611e7c565b90507f0000000000000000000000000000000000000000000000000000000067e08ab364ffffffffff164210158015612d4c5750600154612d499060026131b6565b83115b15612d8357826001546002612d6191906131b6565b6040516387ebe85d60e01b8152600481019290925260248201526044016107b0565b612d95670de0b6b3a7640000836131b6565b612d9f82856131b6565b1115612dc3578281612db9670de0b6b3a7640000856131b6565b612d6191906131cd565b5050600155565b60008083601f840112612ddc57600080fd5b50813567ffffffffffffffff811115612df457600080fd5b602083019150836020828501011115612e0c57600080fd5b9250929050565b60008060008060408587031215612e2957600080fd5b843567ffffffffffffffff811115612e4057600080fd5b8501601f81018713612e5157600080fd5b803567ffffffffffffffff811115612e6857600080fd5b8760208260051b8401011115612e7d57600080fd5b60209182019550935085013567ffffffffffffffff811115612e9e57600080fd5b612eaa87828801612dca565b95989497509550505050565b6001600160a01b03811681146111f957600080fd5b60008060408385031215612ede57600080fd5b8235612ee981612eb6565b946020939093013593505050565b600080600060608486031215612f0c57600080fd5b8335612f1781612eb6565b95602085013595506040909401359392505050565b600060208284031215612f3e57600080fd5b5035919050565b64ffffffffff811681146111f957600080fd5b60008060408385031215612f6b57600080fd5b8235612f7681612eb6565b91506020830135612f8681612f45565b809150509250929050565b600080600060608486031215612fa657600080fd5b505081359360208301359350604090920135919050565b60008060408385031215612fd057600080fd5b50508035926020909101359150565b600080600060608486031215612ff457600080fd5b8335612fff81612eb6565b9250602084013561300f81612eb6565b929592945050506040919091013590565b60006020828403121561303257600080fd5b8135611e3f81612eb6565b60006020828403121561304f57600080fd5b8151611e3f81612eb6565b6001600160a01b0384168152604060208201819052810182905260008360608301825b858110156130ad57823561309081612eb6565b6001600160a01b031682526020928301929091019060010161307d565b509695505050505050565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b634e487b7160e01b600052601160045260246000fd5b808201808211156118aa576118aa6130e7565b60006020828403121561312257600080fd5b81518015158114611e3f57600080fd5b634e487b7160e01b600052600160045260246000fd5b818103818111156118aa576118aa6130e7565b60006020828403121561316d57600080fd5b5051919050565b60006020828403121561318657600080fd5b8151611e3f81612f45565b6000602082840312156131a357600080fd5b815162ffffff81168114611e3f57600080fd5b80820281158282048414176118aa576118aa6130e7565b6000826131ea57634e487b7160e01b600052601260045260246000fd5b500490565b62ffffff82811682821603908111156118aa576118aa6130e7565b6001600160a01b03939093168352602083019190915262ffffff16604082015260600190565b64ffffffffff81811683821601908111156118aa576118aa6130e7565b62ffffff81811683821601908111156118aa576118aa6130e756fe9483a26ad376f30b5199a79e75df3bb05158c4ee32a348f53e83245a5e50c86ea26469706673582212209a1975e04fb13fc1ca52e414c56b5c239b9ac598c02c1ac429d1dfc9e289acd464736f6c634300081a0033

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

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