ETH Price: $1,821.34 (-4.90%)
Gas: 0.07 Gwei
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Adjust235683332025-10-13 10:56:11134 days ago1760352971IN
0xfDeA6C2e...5133A6eeA
0 ETH0.000071340.27935412
Redeem235683272025-10-13 10:54:59134 days ago1760352899IN
0xfDeA6C2e...5133A6eeA
0 ETH0.000066870.26472071
Adjust235683242025-10-13 10:54:23134 days ago1760352863IN
0xfDeA6C2e...5133A6eeA
0 ETH0.000077290.27483929
Pull Local Reser...235682952025-10-13 10:48:35134 days ago1760352515IN
0xfDeA6C2e...5133A6eeA
0 ETH0.000019720.24475149
Adjust235570122025-10-11 20:57:23135 days ago1760216243IN
0xfDeA6C2e...5133A6eeA
0 ETH0.000486443.23453835
Adjust234416332025-09-25 17:46:23151 days ago1758822383IN
0xfDeA6C2e...5133A6eeA
0 ETH0.00136929.18488742
Set Redemption S...233633082025-09-14 19:07:59162 days ago1757876879IN
0xfDeA6C2e...5133A6eeA
0 ETH0.000041530.30529798
Adjust233632462025-09-14 18:55:23162 days ago1757876123IN
0xfDeA6C2e...5133A6eeA
0 ETH0.000097640.2930318
Adjust233332642025-09-10 14:25:23166 days ago1757514323IN
0xfDeA6C2e...5133A6eeA
0 ETH0.001191933.68266423
Adjust233251892025-09-09 11:17:59167 days ago1757416679IN
0xfDeA6C2e...5133A6eeA
0 ETH0.000060840.25750628
Adjust232957912025-09-05 8:43:47172 days ago1757061827IN
0xfDeA6C2e...5133A6eeA
0 ETH0.000090580.31452141
Adjust232885602025-09-04 8:28:47173 days ago1756974527IN
0xfDeA6C2e...5133A6eeA
0 ETH0.000277680.8580441
Set Half Life232759622025-09-02 14:17:35174 days ago1756822655IN
0xfDeA6C2e...5133A6eeA
0 ETH0.000099091.06797824
Adjust232758872025-09-02 14:02:35174 days ago1756821755IN
0xfDeA6C2e...5133A6eeA
0 ETH0.000430121.11173724
Set Redeem Fee B...232756402025-09-02 13:12:59174 days ago1756818779IN
0xfDeA6C2e...5133A6eeA
0 ETH0.000036120.53151354
Set Half Life232756382025-09-02 13:12:35174 days ago1756818755IN
0xfDeA6C2e...5133A6eeA
0 ETH0.000038780.56527524

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Method Block
From
To
0x61018060232756162025-09-02 13:08:11174 days ago1756818491  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 0x783c9887...849170aC0
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
Lender

Compiler Version
v0.8.13+commit.abaa5c0e

Optimization Enabled:
Yes with 200 runs

Other Settings:
london EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.13;

import "lib/solmate/src/tokens/ERC20.sol";
import "lib/solmate/src/utils/SafeTransferLib.sol";
import "lib/solmate/src/utils/FixedPointMathLib.sol";
import "./Coin.sol";
import "./Vault.sol";
import "./InterestModel.sol";

interface IChainlinkFeed {
    function decimals() external view returns (uint8);
    function latestRoundData() external view returns (
        uint80 roundId,
        int256 answer,
        uint256 startedAt,
        uint256 updatedAt,
        uint80 answeredInRound
    );
}

interface IFactory {
    function getFeeOf(address _lender) external view returns (uint256);
}

contract Lender {

    using SafeTransferLib for ERC20;
    using FixedPointMathLib for uint256;

    // single 256-bit slot
    uint16 public targetFreeDebtRatioStartBps = 2000; // max uint16 is 65535 bps which is outside of the range [0, 10000]
    uint16 public targetFreeDebtRatioEndBps = 4000; // max uint16 is 65535 bps which is outside of the range [0, 10000]
    uint16 public redeemFeeBps = 30; // max uint16 is 65535 bps fee which is outside of the range [0, 10000]
    uint64 public expRate = uint64(uint(wadLn(2*1e18)) / 7 days); // max result is 693147180559945309 which is within uint64 range
    uint40 public lastAccrue; // max uint40 is year 36812
    uint88 public lastBorrowRateMantissa = uint88(5e15); // max uint88 is equivalent to 309485000% APR
    uint16 public feeBps; // max uint16 is 65535 bps which is outside of the range [0, 10000]

    // single 256-bit slot
    uint16 public cachedGlobalFeeBps;
    uint120 public accruedLocalReserves;
    uint120 public accruedGlobalReserves;

    // Other state variables
    address public operator;
    address public pendingOperator;
    uint public immutabilityDeadline; // may only be reduced by operator
    uint public totalFreeDebt;
    uint public totalFreeDebtShares;
    uint public totalPaidDebt;
    uint public totalPaidDebtShares;
    uint public epoch;

    // Constants and immutables
    Coin public immutable coin;
    ERC20 public immutable collateral;
    IChainlinkFeed public immutable feed;
    Vault public immutable vault;
    InterestModel public immutable interestModel;
    IFactory public immutable factory;
    uint public immutable collateralFactor;
    uint public immutable minDebt;
    uint public constant STALENESS_THRESHOLD = 25 hours; // standard 24 hours staleness + 1 hour buffer
    uint public constant STALENESS_UNWIND_DURATION = 24 hours;
    uint public constant MIN_LIQUIDATION_DEBT = 10_000e18; // 10,000 Coin

    // Mappings
    mapping(address => uint) public _cachedCollateralBalances; // should not be read externally in most cases
    mapping(address => uint) public freeDebtShares;
    mapping(address => uint) public paidDebtShares;
    mapping(address => bool) public isRedeemable;
    mapping(address => mapping(address => bool)) public delegations;

    mapping(address => uint) public borrowerLastRedeemedIndex;
    mapping(address => uint) public borrowerEpoch;
    mapping(uint => uint) public epochRedeemedCollateral;
    uint256 public nonRedeemableCollateral;

    constructor(
        ERC20 _collateral,
        IChainlinkFeed _feed,
        Coin _coin,
        Vault _vault,
        InterestModel _interestModel,
        IFactory _factory,
        address _operator,
        uint _collateralFactor,
        uint _minDebt,
        uint _timeUntilImmutability
    ) {
        require(_collateralFactor <= 10000, "Invalid collateral factor");
        require(_timeUntilImmutability < 1460 days, "Max immutability deadline is in 4 years");
        collateral = _collateral;
        feed = _feed;
        coin = _coin;
        vault = _vault;
        interestModel = _interestModel;
        factory = _factory;
        operator = _operator;
        collateralFactor = _collateralFactor;
        minDebt = _minDebt;
        immutabilityDeadline = block.timestamp + _timeUntilImmutability;
        lastAccrue = uint40(block.timestamp);
        cachedGlobalFeeBps = uint16(factory.getFeeOf(address(this)));
    }

    // Modifiers
    
    modifier onlyOperator() {
        require(msg.sender == operator, "Unauthorized");
        _;
    }

    modifier beforeDeadline() {
        require(block.timestamp < immutabilityDeadline, "Deadline passed");
        _;
    }

    // Public functions

    function accrueInterest() public {
        uint timeElapsed = block.timestamp - lastAccrue;
        if(timeElapsed == 0) return;

        try interestModel.calculateInterest(
            totalPaidDebt,
            lastBorrowRateMantissa,
            timeElapsed,
            expRate,
            getFreeDebtRatio(),
            targetFreeDebtRatioStartBps,
            targetFreeDebtRatioEndBps
        ) returns (uint currBorrowRate, uint interest) {
            uint120 localReserveFee = uint120(interest * feeBps / 10000);
            uint120 globalReserveFee = uint120(interest * cachedGlobalFeeBps / 10000);
            accruedLocalReserves += localReserveFee;
            accruedGlobalReserves += globalReserveFee;
            // we remove reserve fees from interest before calculating how much to give to stakers
            uint interestAfterFees = interest - localReserveFee - globalReserveFee;
            uint totalStaked = vault.totalAssets();
            if(totalStaked < totalPaidDebt) { // this also implies totalPaidDebt > 0 and guards the division below
                // if total staked is less than paid debt, giving all interest to stakers would
                // result in higher supply rate than borrow rate which is undesirable.
                // we cap the supply rate at the borrow rate and give the rest to local reserves.
                uint stakedInterest = interestAfterFees * totalStaked / totalPaidDebt;
                coin.mint(address(vault), stakedInterest);
                uint remainingInterest = interestAfterFees - stakedInterest;
                accruedLocalReserves += uint120(remainingInterest);
            } else {
                // if total staked is greater than paid debt, we give all interest to stakers
                coin.mint(address(vault), interestAfterFees);
            }
            totalPaidDebt += interest; // we add all interest to paid debt (NOT interestAfterFees)
            lastAccrue = uint40(block.timestamp);
            lastBorrowRateMantissa = uint88(currBorrowRate);
            cachedGlobalFeeBps = uint16(factory.getFeeOf(address(this)));
        } catch {
            // If the call reverts, do nothing.
        }
    }

    function adjust(address account, int collateralDelta, int debtDelta) public {
        accrueInterest();
        updateBorrower(account);
        // Handle collateral changes
        if (collateralDelta > 0) {
            if(!isRedeemable[account]) nonRedeemableCollateral += uint(collateralDelta);
            
            // Deposit collateral
            _cachedCollateralBalances[account] += uint(collateralDelta);
            collateral.safeTransferFrom(msg.sender, address(this), uint(collateralDelta));
        } else if (collateralDelta < 0) {
            // Ensure sufficient collateral for non-redeemable accounts
            if (isRedeemable[account]) {
                require(
                    collateral.balanceOf(address(this)) - uint256(-collateralDelta) >= nonRedeemableCollateral,
                    "Insufficient redeemable collateral"
                );
            } else {
                nonRedeemableCollateral -= uint256(-collateralDelta);
            }
 
            // Withdraw collateral
            _cachedCollateralBalances[account] -= uint(-collateralDelta);
            collateral.safeTransfer(msg.sender, uint(-collateralDelta));
        }

        // Handle debt changes
        if (debtDelta > 0) {
            // Borrow
            uint amount = uint256(debtDelta);
            increaseDebt(account, amount);
            coin.mint(msg.sender, amount);
        } else if (debtDelta < 0) {
            // Repay
            uint amount = uint256(-debtDelta);
            uint debt = getDebtOf(account);
            if(debt <= amount) {
                amount = debt;
                decreaseDebt(account, type(uint).max);
            } else {
                decreaseDebt(account, amount);
            }
            coin.transferFrom(msg.sender, address(this), amount);
            coin.burn(amount);
        }

        // if debtDelta is non-zero, require debt balance to either be 0 or >= minDebt
        uint debtBalance = getDebtOf(account);
        if(debtDelta != 0) require(debtBalance == 0 || debtBalance >= minDebt, "Debt below minimum and larger than 0");

        // Emit event before the first early return
        emit PositionAdjusted(account, collateralDelta, debtDelta);

        // Skip remaining invariants if caller does not reduce collateral AND does not increase debt
        if(collateralDelta >= 0 && debtDelta <= 0) return;

        // The caller is removing collateral and/or increasing debt. Enforce ownership beyond this point
        require(msg.sender == account || delegations[account][msg.sender], "Unauthorized");

        // Skip solvency checks if debt is zero
        if(debtBalance == 0) return;

        // Check solvency
        (uint price, bool reduceOnly, ) = getCollateralPrice();
        require(!reduceOnly, "Reduce only");
        uint borrowingPower = price * _cachedCollateralBalances[account] * collateralFactor / 1e18 / 10000;
        require(debtBalance <= borrowingPower, "Solvency check failed");
    }

    function adjust(address account, int collateralDelta, int debtDelta, bool chooseRedeemable) external {
        setRedemptionStatus(account, chooseRedeemable);
        adjust(account, collateralDelta, debtDelta);
    }

    /// @notice Allows an account to delegate control of their position to another address (adjustPosition, optInRedemptions, optOutRedemptions functions)
    /// @param delegatee The address to delegate to
    /// @param isDelegatee True to enable delegation, false to revoke
    function delegate(address delegatee, bool isDelegatee) external {
        delegations[msg.sender][delegatee] = isDelegatee;
        emit DelegationUpdated(msg.sender, delegatee, isDelegatee);
    }

    function setRedemptionStatus(address account, bool chooseRedeemable) public {
        accrueInterest();
        updateBorrower(account);
        require(msg.sender == account || delegations[account][msg.sender], "Unauthorized");
        if(chooseRedeemable == isRedeemable[account]) return; // no change
        if(chooseRedeemable){
            borrowerEpoch[account] = epoch;
            borrowerLastRedeemedIndex[account] = epochRedeemedCollateral[epoch];
            nonRedeemableCollateral -= _cachedCollateralBalances[account];
        } else {
            nonRedeemableCollateral += _cachedCollateralBalances[account];
        }
        uint prevDebt = getDebtOf(account);
        if(prevDebt > 0) {
            decreaseDebt(account, type(uint).max);
            isRedeemable[account] = chooseRedeemable;
            increaseDebt(account, prevDebt);
            uint currDebt = getDebtOf(account);
            require(currDebt >= prevDebt, "Debt decreased unexpectedly");
        } else {
            isRedeemable[account] = chooseRedeemable;
        }
        emit RedemptionStatusUpdated(account, chooseRedeemable);
    }

        /// @notice Liquidates an unsafe position
    /// @param borrower The account to be liquidated
    /// @param repayAmount The amount of debt to repay
    /// @param minCollateralOut The minimum amount of collateral to receive
    /// @return The amount of collateral received
    function liquidate(address borrower, uint repayAmount, uint minCollateralOut) external returns(uint) {
        accrueInterest();
        updateBorrower(borrower);
        require(repayAmount > 0, "Repay amount must be greater than 0");
        (uint price,, bool allowLiquidations) = getCollateralPrice();
        require(allowLiquidations, "liquidations disabled");
        uint debt = getDebtOf(borrower);
        uint collateralBalance = _cachedCollateralBalances[borrower];
        // check liquidation condition
        uint liquidatableDebt = getLiquidatableDebt(collateralBalance, price, debt);
        require(liquidatableDebt > 0, "insufficient liquidatable debt");
        if(repayAmount > liquidatableDebt) {
            repayAmount = liquidatableDebt;
        }

        // apply repayment
        decreaseDebt(borrower, repayAmount);

        // calculate collateral reward
        uint liqIncentiveBps = getLiquidationIncentiveBps(collateralBalance, price, debt);
        uint collateralRewardValue = repayAmount * (10000 + liqIncentiveBps) / 10000;
        uint collateralReward = collateralRewardValue * 1e18 / price;
        collateralReward = collateralReward > collateralBalance ? collateralBalance : collateralReward;
        require(collateralReward >= minCollateralOut, "insufficient collateral out");

        if(collateralReward > 0) {
            collateral.safeTransfer(msg.sender, collateralReward);
            _cachedCollateralBalances[borrower] = collateralBalance - collateralReward;
            if(!isRedeemable[borrower]) nonRedeemableCollateral -= collateralReward;
        }
        coin.transferFrom(msg.sender, address(this), repayAmount);
        coin.burn(repayAmount);
        emit Liquidated(borrower, msg.sender, repayAmount, collateralReward);
        // try to write off remaining debt. Call externally and catch error to prevent liquidation failure
        try this.writeOff(borrower, msg.sender) {} catch {}
        return collateralReward;
    }

    /// @notice Redistributes excess debt of undercollateralized accounts among other borrowers
    /// @param borrower The account in potentiallyundercollateralized state
    /// @return writtenOff True if the borrower was written off, false otherwise
    /// @param to The address to send the collateral to
    /// @dev This function is called by liquidate() when a borrower's position is undercollateralized. It should never revert to avoid liquidation failure.
    function writeOff(address borrower, address to) external returns (bool writtenOff) {
        accrueInterest();
        updateBorrower(borrower);
        // check for write off
        uint debt = getDebtOf(borrower);
        if(debt > 0) {
            uint collateralBalance = _cachedCollateralBalances[borrower];
            (uint price,, bool allowLiquidations) = getCollateralPrice();
            require(allowLiquidations, "liquidations disabled");
            uint collateralValue = price * collateralBalance / 1e18;
            // if debt is more than 100 times the collateral value, write off
            if(debt > collateralValue * 100) {
                // 1. delete all of the borrower's debt
                decreaseDebt(borrower, type(uint).max);
                // 2. redistribute excess debt among remaining borrowers
                uint256 totalDebt = totalFreeDebt + totalPaidDebt;
                if (totalDebt > 0) {
                    uint256 freeDebtIncrease = debt * totalFreeDebt / totalDebt;
                    uint256 paidDebtIncrease = debt - freeDebtIncrease;

                    totalFreeDebt += freeDebtIncrease;
                    totalPaidDebt += paidDebtIncrease;
                }
                // 3. send collateral to caller
                collateral.safeTransfer(to, collateralBalance);
                if(!isRedeemable[borrower]) nonRedeemableCollateral -= collateralBalance;
                
                _cachedCollateralBalances[borrower] = 0;
                emit WrittenOff(borrower, to, debt, collateralBalance);
                writtenOff = true;
            }
        }
    }

    /// @notice Redeems Coin for collateral at current market price minus a fee
    /// @param amountIn The amount of Coin to redeem
    /// @param minAmountOut The minimum amount of collateral to receive
    /// @return amountOut The amount of collateral received
    /// @dev Redemptions requires sufficient redeemable collateral to seize and free debt to repay
    function redeem(uint amountIn, uint minAmountOut) external returns (uint amountOut) {
        accrueInterest();
        // calculate amountOut
        amountOut = getRedeemAmountOut(amountIn);
        require(amountOut >= minAmountOut, "insufficient amount out");
        require(collateral.balanceOf(address(this)) - amountOut >= nonRedeemableCollateral, "Insufficient redeemable collateral");
        // repay on behalf of free debtors
        totalFreeDebt -= amountIn;
        coin.transferFrom(msg.sender, address(this), amountIn);
        coin.burn(amountIn);

        // distribute collateral redemption per free debt share
        epochRedeemedCollateral[epoch] += amountOut.mulDivUp(1e36, totalFreeDebtShares);

        collateral.safeTransfer(msg.sender, amountOut);

        // Intentional division by zero and revert if totalFreeDebt is 0
        if( totalFreeDebtShares / totalFreeDebt > 1e9) {
            epoch++;
            totalFreeDebtShares = totalFreeDebtShares.mulDivUp(1e18,1e36); 
            emit NewEpoch(epoch);
        }

        emit Redeemed(msg.sender, amountIn, amountOut);
        return amountOut;
    }

    // Internal functions

    function updateBorrower(address borrower) internal {
        uint borrowerDebtShares = freeDebtShares[borrower];
        
        if (borrowerDebtShares > 0) {
            uint _borrowerEpoch = borrowerEpoch[borrower];
            uint bal = _cachedCollateralBalances[borrower];
            uint lastIndex = borrowerLastRedeemedIndex[borrower];
            // Loop through missed epochs (max 5 iterations considering max uint256 is 2^256 - 1 would go to zero in 5 iterations)
            for (uint i = 0; i < 5 && _borrowerEpoch < epoch && borrowerDebtShares > 0; ++i) {
                // Apply redemption for the borrower's current epoch
                uint indexDelta = epochRedeemedCollateral[_borrowerEpoch] - lastIndex;
                uint redeemedCollateral = indexDelta.mulDivUp(borrowerDebtShares, 1e36);
                bal = bal < redeemedCollateral ? 0 : bal - redeemedCollateral;

                // Move to next epoch, reduce shares
                _borrowerEpoch += 1;
                borrowerDebtShares = borrowerDebtShares.divWadUp(1e36) == 1 ? 0 : borrowerDebtShares.divWadUp(1e36); // If shares is 1 round down to 0
                lastIndex = 0; // For new epoch, last redeemed index is 0
            }
            // Apply any remaining redemption for the current epoch
            if (borrowerDebtShares > 0) {
                uint indexDelta = epochRedeemedCollateral[_borrowerEpoch] - lastIndex;
                uint redeemedCollateral = indexDelta.mulDivUp(borrowerDebtShares, 1e36);
                bal = bal < redeemedCollateral ? 0 : bal - redeemedCollateral;
            }
            // Update state
            freeDebtShares[borrower] = borrowerDebtShares;
            _cachedCollateralBalances[borrower] = bal;
        }

        if(isRedeemable[borrower]){
            borrowerEpoch[borrower] = epoch;
            borrowerLastRedeemedIndex[borrower] = epochRedeemedCollateral[epoch];
        } 
    }

    function increaseDebt(address account, uint256 amount) internal {
        if (isRedeemable[account]) {
            // Handle free debt
            uint shares = totalFreeDebtShares == 0 ? 
                    amount : 
                    amount.mulDivUp(totalFreeDebtShares, totalFreeDebt);
            totalFreeDebt += amount;
            totalFreeDebtShares += shares;
            freeDebtShares[account] += shares;
        } else {
            // Handle paid debt 
            uint256 shares = totalPaidDebtShares == 0 ? 
                amount : 
                amount.mulDivUp(totalPaidDebtShares, totalPaidDebt);
            totalPaidDebt += amount;
            totalPaidDebtShares += shares;
            paidDebtShares[account] += shares;
        }
    }

    function decreaseDebt(address account, uint256 amount) internal {
        if (isRedeemable[account]) {
            // Handle free debt
            uint256 shares;
            if(amount == type(uint).max) {
                shares = freeDebtShares[account];
                amount = getDebtOf(account);
            } else {
                shares = amount.mulDivDown(totalFreeDebtShares, totalFreeDebt);
            }
            freeDebtShares[account] -= shares;
            totalFreeDebtShares = totalFreeDebtShares <= shares ? 0 : totalFreeDebtShares - shares; // prevent underflow
            totalFreeDebt = totalFreeDebt <= amount ? 0 : totalFreeDebt - amount; // prevent underflow
        } else {
            // Handle paid debt
            uint256 shares;
            if(amount == type(uint).max) {
                shares = paidDebtShares[account];
                amount = getDebtOf(account);
            } else {
                shares = amount.mulDivDown(totalPaidDebtShares, totalPaidDebt);
            }
            
            paidDebtShares[account] -= shares;
            totalPaidDebtShares -= shares;
            totalPaidDebt -= amount;
        }
    }

    function getLiquidatableDebt(uint collateralBalance, uint price, uint debt) internal view returns(uint liquidatableDebt){
        uint borrowingPower = price * collateralBalance * collateralFactor / 1e18 / 10000;
        if(borrowingPower > debt) return 0;
        // liquidate 25% of the total debt
        liquidatableDebt = debt / 4; // 25% of the debt
        // liquidate at least MIN_LIQUIDATION_DEBT (or the entire debt if it's less than MIN_LIQUIDATION_DEBT)
        if(liquidatableDebt < MIN_LIQUIDATION_DEBT) liquidatableDebt = debt < MIN_LIQUIDATION_DEBT ? debt : MIN_LIQUIDATION_DEBT;
    }

    function getLiquidationIncentiveBps(uint collateralBalance, uint price, uint debt) internal view returns(uint) {
        uint collateralValue = collateralBalance * price / 1e18;
        if (collateralValue == 0) return 100; // avoid division by zero, return 1% incentive
        uint ltvBps = debt * 10000 / collateralValue;
        uint _collateralFactor = collateralFactor; // gas optimization
        uint maxLtvBps = _collateralFactor + 500; // range is [_collateralFactor, _collateralFactor + 5%]

        if (ltvBps <= _collateralFactor) {
            return 100; // 1% incentive
        } else if (ltvBps >= maxLtvBps) {
            return 1000; // 10% incentive
        } else {
            // linear interpolation between 1% and 10% incentive
            return 100 + (ltvBps - _collateralFactor) * 900 / (maxLtvBps - _collateralFactor);
        }
    }

    // Getters

    function getFreeDebtRatio() public view returns (uint) {
        return totalFreeDebt == 0 ? 0 : totalFreeDebt * 10000 / (totalPaidDebt + totalFreeDebt);
    }

    function getDebtOf(address account) public view returns (uint) {
        if(isRedeemable[account]) {
            return totalFreeDebtShares == 0 ? 0 : freeDebtShares[account].mulDivUp(totalFreeDebt, totalFreeDebtShares);
        } else {
            return totalPaidDebtShares == 0 ? 0 : paidDebtShares[account].mulDivUp(totalPaidDebt, totalPaidDebtShares);
        }
    }

    /// @notice Gets the current price of the collateral asset
    /// @return price The price in USD normalized to (36 - collateral decimals) decimals for consistent calculations
    /// @return reduceOnly A boolean indicating if reduce only mode is enabled
    /// @return allowLiquidations A boolean indicating if liquidations and write-offs are enabled
    function getCollateralPrice() public view returns (uint price, bool reduceOnly, bool allowLiquidations) {
        uint updatedAt;
        allowLiquidations = true; // Default to allowing liquidations
        
        // call our own getFeedPrice() externally to catch all feed reverts e.g. due to inexistent feed contract, function, etc.
        try this.getFeedPrice() returns (uint _price, uint _updatedAt) {
            price = _price;
            updatedAt = _updatedAt;
            if(price == 0) {
                reduceOnly = true;
                allowLiquidations = false; // Disable liquidations if price is invalid
            }
        } catch {
            reduceOnly = true;
            allowLiquidations = false; // Disable liquidations only if the oracle feed is reverting
        }
        
        uint currentTime = block.timestamp;
        uint timeElapsed = currentTime >= updatedAt ? currentTime - updatedAt : 0;

        if (timeElapsed > STALENESS_THRESHOLD) {
            reduceOnly = true;
            uint stalenessDuration = timeElapsed - STALENESS_THRESHOLD;
            if (stalenessDuration < STALENESS_UNWIND_DURATION) {
                price = price * (STALENESS_UNWIND_DURATION - stalenessDuration) / STALENESS_UNWIND_DURATION;
            } else {
                price = 0;
            }
        }
        price = price == 0 ? 1 : price; // avoid division by zero in consumer functions
    }

    function getFeedPrice() external view returns (uint price, uint updatedAt) {
        (,int256 feedPrice,,uint256 feedUpdatedAt,) = feed.latestRoundData();
        uint8 feedDecimals = feed.decimals();
        uint8 tokenDecimals = collateral.decimals();
        if(feedDecimals + tokenDecimals <= 36) {
            uint8 decimals = 36 - tokenDecimals - feedDecimals;
            price = feedPrice > 0 ? uint(feedPrice) * (10**decimals) : 0; // convert negative price to uint 0 to signal invalid price
        } else {
            uint8 decimals = feedDecimals + tokenDecimals - 36;
            price = feedPrice > 0 ? uint(feedPrice) / (10**decimals) : 0; // convert negative price to uint 0 to signal invalid price
        }
        updatedAt = feedUpdatedAt;
    }

    /// @notice Calculates the amount of collateral received for redeeming Coin
    /// @param amountIn The amount of Coin to redeem
    /// @return amountOut The amount of collateral to receive
    function getRedeemAmountOut(uint amountIn) public view returns (uint amountOut) {
        if(amountIn > totalFreeDebt) return 0; // can't redeem more than free debt
        (uint price,, bool allowLiquidations) = getCollateralPrice();
        if(!allowLiquidations) return 0;
        // multiply amountIn by price then apply redeem fee to amountIn
        amountOut = amountIn * 1e18 * (10000 - redeemFeeBps) / price / 10000;
    }

    // Setters

    function setHalfLife(uint64 halfLife) external onlyOperator beforeDeadline {
        accrueInterest();
        require(halfLife >= 12 hours && halfLife <= 30 days, "Invalid half life");
        expRate = uint64(uint(wadLn(2*1e18)) / halfLife);
        emit HalfLifeUpdated(halfLife);
    }

    function setTargetFreeDebtRatio(uint16 startBps, uint16 endBps) external onlyOperator beforeDeadline {
        accrueInterest();
        require(startBps >= 500 && startBps <= endBps, "Invalid start bps");
        require(endBps >= startBps && endBps <= 9500, "Invalid end bps");
        targetFreeDebtRatioStartBps = uint16(startBps);
        targetFreeDebtRatioEndBps = uint16(endBps);
        emit TargetFreeDebtRatioUpdated(startBps, endBps);
    }

    function setRedeemFeeBps(uint16 _redeemFeeBps) external onlyOperator beforeDeadline {
        accrueInterest();
        require(_redeemFeeBps <= 300, "Invalid redeem fee bps");
        redeemFeeBps = uint16(_redeemFeeBps);
        emit RedeemFeeBpsUpdated(_redeemFeeBps);
    }

    function setLocalReserveFeeBps(uint _feeBps) external onlyOperator {
        accrueInterest();
        require(_feeBps <= 1000, "Invalid fee");
        feeBps = uint16(_feeBps);
        emit LocalReserveFeeUpdated(_feeBps);
    }

    function setPendingOperator(address _pendingOperator) external onlyOperator {
        pendingOperator = _pendingOperator;
        emit PendingOperatorUpdated(_pendingOperator);
    }

    function acceptOperator() external {
        require(msg.sender == pendingOperator, "Unauthorized");
        operator = pendingOperator;
        emit OperatorAccepted(pendingOperator);
    }

    function enableImmutabilityNow() external onlyOperator beforeDeadline {
        immutabilityDeadline = block.timestamp;
    }

    function pullLocalReserves() external onlyOperator {
        accrueInterest();
        coin.mint(msg.sender, accruedLocalReserves);
        accruedLocalReserves = 0;
    }

    function pullGlobalReserves(address _to) external {
        require(msg.sender == address(factory), "Unauthorized");
        accrueInterest();
        coin.mint(_to, accruedGlobalReserves);
        accruedGlobalReserves = 0;
    }

    // Events

    event PositionAdjusted(address indexed account, int collateralDelta, int debtDelta);
    event HalfLifeUpdated(uint64 halfLife);
    event TargetFreeDebtRatioUpdated(uint16 startBps, uint16 endBps);
    event RedeemFeeBpsUpdated(uint16 redeemFeeBps);
    event DelegationUpdated(address indexed delegator, address indexed delegatee, bool isDelegatee);
    event PendingOperatorUpdated(address indexed pendingOperator);
    event OperatorAccepted(address indexed operator);
    event LocalReserveFeeUpdated(uint256 feeBps);
    event RedemptionStatusUpdated(address indexed account, bool isRedeemable);
    event Liquidated(address indexed borrower, address indexed liquidator, uint repayAmount, uint collateralOut);
    event WrittenOff(address indexed borrower, address indexed to, uint debt, uint collateral);
    event NewEpoch(uint epoch);
    event Redeemed(address indexed account, uint amountIn, uint amountOut);
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

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

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

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

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

    string public name;

    string public symbol;

    uint8 public immutable decimals;

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

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

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

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

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

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

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

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

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

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

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

        return true;
    }

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

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

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

        return true;
    }

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

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

        balanceOf[from] -= amount;

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

        emit Transfer(from, to, amount);

        return true;
    }

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

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

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

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

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

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

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

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

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
    /*//////////////////////////////////////////////////////////////
                             ETH OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferETH(address to, uint256 amount) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and store if it succeeded or not.
            success := call(gas(), to, amount, 0, 0, 0, 0)
        }

        require(success, "ETH_TRANSFER_FAILED");
    }

    /*//////////////////////////////////////////////////////////////
                            ERC20 OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferFrom(
        ERC20 token,
        address from,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
            mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
            )
        }

        require(success, "TRANSFER_FROM_FAILED");
    }

    function safeTransfer(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "TRANSFER_FAILED");
    }

    function safeApprove(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "APPROVE_FAILED");
    }
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLib {
    /*//////////////////////////////////////////////////////////////
                    SIMPLIFIED FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    uint256 internal constant MAX_UINT256 = 2**256 - 1;

    uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.

    function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
    }

    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
    }

    function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
    }

    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
    }

    /*//////////////////////////////////////////////////////////////
                    LOW LEVEL FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function mulDivDown(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // Divide x * y by the denominator.
            z := div(mul(x, y), denominator)
        }
    }

    function mulDivUp(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // If x * y modulo the denominator is strictly greater than 0,
            // 1 is added to round up the division of x * y by the denominator.
            z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
        }
    }

    function rpow(
        uint256 x,
        uint256 n,
        uint256 scalar
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            switch x
            case 0 {
                switch n
                case 0 {
                    // 0 ** 0 = 1
                    z := scalar
                }
                default {
                    // 0 ** n = 0
                    z := 0
                }
            }
            default {
                switch mod(n, 2)
                case 0 {
                    // If n is even, store scalar in z for now.
                    z := scalar
                }
                default {
                    // If n is odd, store x in z for now.
                    z := x
                }

                // Shifting right by 1 is like dividing by 2.
                let half := shr(1, scalar)

                for {
                    // Shift n right by 1 before looping to halve it.
                    n := shr(1, n)
                } n {
                    // Shift n right by 1 each iteration to halve it.
                    n := shr(1, n)
                } {
                    // Revert immediately if x ** 2 would overflow.
                    // Equivalent to iszero(eq(div(xx, x), x)) here.
                    if shr(128, x) {
                        revert(0, 0)
                    }

                    // Store x squared.
                    let xx := mul(x, x)

                    // Round to the nearest number.
                    let xxRound := add(xx, half)

                    // Revert if xx + half overflowed.
                    if lt(xxRound, xx) {
                        revert(0, 0)
                    }

                    // Set x to scaled xxRound.
                    x := div(xxRound, scalar)

                    // If n is even:
                    if mod(n, 2) {
                        // Compute z * x.
                        let zx := mul(z, x)

                        // If z * x overflowed:
                        if iszero(eq(div(zx, x), z)) {
                            // Revert if x is non-zero.
                            if iszero(iszero(x)) {
                                revert(0, 0)
                            }
                        }

                        // Round to the nearest number.
                        let zxRound := add(zx, half)

                        // Revert if zx + half overflowed.
                        if lt(zxRound, zx) {
                            revert(0, 0)
                        }

                        // Return properly scaled zxRound.
                        z := div(zxRound, scalar)
                    }
                }
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                        GENERAL NUMBER UTILITIES
    //////////////////////////////////////////////////////////////*/

    function sqrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            let y := x // We start y at x, which will help us make our initial estimate.

            z := 181 // The "correct" value is 1, but this saves a multiplication later.

            // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
            // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.

            // We check y >= 2^(k + 8) but shift right by k bits
            // each branch to ensure that if x >= 256, then y >= 256.
            if iszero(lt(y, 0x10000000000000000000000000000000000)) {
                y := shr(128, y)
                z := shl(64, z)
            }
            if iszero(lt(y, 0x1000000000000000000)) {
                y := shr(64, y)
                z := shl(32, z)
            }
            if iszero(lt(y, 0x10000000000)) {
                y := shr(32, y)
                z := shl(16, z)
            }
            if iszero(lt(y, 0x1000000)) {
                y := shr(16, y)
                z := shl(8, z)
            }

            // Goal was to get z*z*y within a small factor of x. More iterations could
            // get y in a tighter range. Currently, we will have y in [256, 256*2^16).
            // We ensured y >= 256 so that the relative difference between y and y+1 is small.
            // That's not possible if x < 256 but we can just verify those cases exhaustively.

            // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
            // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
            // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.

            // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
            // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.

            // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
            // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.

            // There is no overflow risk here since y < 2^136 after the first branch above.
            z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.

            // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))

            // If x+1 is a perfect square, the Babylonian method cycles between
            // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
            // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
            // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
            // If you don't care whether the floor or ceil square root is returned, you can remove this statement.
            z := sub(z, lt(div(x, z), z))
        }
    }

    function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Mod x by y. Note this will return
            // 0 instead of reverting if y is zero.
            z := mod(x, y)
        }
    }

    function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // Divide x by y. Note this will return
            // 0 instead of reverting if y is zero.
            r := div(x, y)
        }
    }

    function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Add 1 to x * y if x % y > 0. Note this will
            // return 0 instead of reverting if y is zero.
            z := add(gt(mod(x, y), 0), div(x, y))
        }
    }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.13;

import "lib/solmate/src/tokens/ERC20.sol";

contract Coin is ERC20 {

    address public immutable minter;

    constructor(address _minter, string memory name, string memory symbol) ERC20(name, symbol, 18) {
        minter = _minter;
    }

    function mint(address to, uint256 amount) external {
        require(msg.sender == minter, "Only minter can mint");
        _mint(to, amount);
    }

    function burn(uint256 amount) external {
        _burn(msg.sender, amount);
    }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.13;

import "lib/solmate/src/tokens/ERC4626.sol";

interface ILender {
    function accrueInterest() external;
    function coin() external view returns (ERC20);
}

contract Vault is ERC4626 {

    ILender public immutable lender;
    uint256 constant MIN_SHARES = 1e16; // 1 cent;

    /// @param _name Name of the token. Prepended with "Staked "
    /// @param _symbol Symbol of the token. Prepended with "s"
    /// @param _lender Address of the Lender token
    constructor(
        string memory _name,
        string memory _symbol,
        address _lender
    ) ERC4626(
        ILender(_lender).coin(),
        string.concat("Staked ", _name),
        string.concat("s", _symbol)
    ) {
        lender = ILender(_lender);
    }

    modifier accrueInterest() {
        lender.accrueInterest();
        _;
    }

    function totalAssets() public view override returns (uint256) {
        return asset.balanceOf(address(this));
    }

    /// @notice Deposits assets into the vault
    /// @param assets Amount of assets to deposit
    /// @param receiver Address to receive the shares
    /// @return shares Amount of shares minted
    function deposit(uint256 assets, address receiver) public accrueInterest override returns (uint256 shares) {
        bool isFirstDeposit = totalSupply == 0;
        shares = super.deposit(assets, receiver);
        if(isFirstDeposit) {
            // if this underflows, the first deposit is less than MIN_SHARES which is not allowed
            balanceOf[receiver] -= MIN_SHARES;
            balanceOf[address(0)] += MIN_SHARES;
            emit Transfer(receiver, address(0), MIN_SHARES);
            shares -= MIN_SHARES;
        }
    }

    /// @notice Mints shares of the vault
    /// @param shares Amount of shares to mint
    /// @param receiver Address to receive the shares
    /// @return assets Amount of assets deposited
    function mint(uint256 shares, address receiver) public accrueInterest override returns (uint256 assets) {
        bool isFirstDeposit = totalSupply == 0;
        assets = super.mint(shares, receiver);
        if(isFirstDeposit) {
            // if this underflows, the first deposit is less than MIN_SHARES which is not allowed
            balanceOf[receiver] -= MIN_SHARES;
            balanceOf[address(0)] += MIN_SHARES;
            emit Transfer(receiver, address(0), MIN_SHARES);
            assets -= MIN_SHARES; // shares and assets are 1:1 when the first deposit is made
        }
    }

    /// @notice Withdraws assets from the vault
    /// @param assets Amount of assets to withdraw
    /// @param receiver Address to receive the assets
    /// @param owner Owner of the shares
    /// @return shares Amount of shares burned
    function withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) public accrueInterest override returns (uint256 shares) {
        shares = super.withdraw(assets, receiver, owner);
    }

    /// @notice Redeems shares for assets
    /// @param shares Amount of shares to redeem
    /// @param receiver Address to receive the assets
    /// @param owner Owner of the shares
    /// @return assets Amount of assets withdrawn
    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) public accrueInterest override returns (uint256 assets) {
        assets = super.redeem(shares, receiver, owner);
    }

}

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.13;

import "lib/solmate/src/utils/SignedWadMath.sol";

// Only one instance of this contract is deployed for use by all Lender contracts
contract InterestModel {

    uint internal constant MIN_RATE = 5e15; // 0.5%

    // The reason why this pure math is called externally by Lender contracts even though
    // it is non-upgradeable is to allow Lender contracts try/catch the call for safety.
    // In case of unexpected under/overflow here, Lender contracts would skip accruing interest
    // while allowing borrowers to exit. Otherwise failure would freeze their funds.
    // We could have also added this as an external function to Lender and called
    // self.calculateInterest() externally, but since it's the same cost to call it here,
    // we opt for this approach in order to reduce contract bytecode size of Lender.
    // We also reduce Factory deployment costs by only using one instance of
    // this contract for all Lender contracts.
    function calculateInterest(
        uint _totalPaidDebt,
        uint _lastRate,
        uint _timeElapsed,
        uint _expRate,
        uint _lastFreeDebtRatioBps,
        uint _targetFreeDebtRatioStartBps,
        uint _targetFreeDebtRatioEndBps
    ) external pure returns (uint currBorrowRate, uint interest) {
        // check _expRate * _timeElapsed overflow
        if(uint(type(int256).max) / _expRate < _timeElapsed) _timeElapsed = uint(type(int256).max) / _expRate;
        // we use a negative exponent in order to prevent growthDecay overflow due to large timeElapsed
        // Results of positive exponents can exceed max uint256, negative exponents only return a value between [0, 1e18]
        uint growthDecay = uint(wadExp(-int(_expRate * _timeElapsed)));
        
        if (_lastFreeDebtRatioBps < _targetFreeDebtRatioStartBps) {
            currBorrowRate = _lastRate * 1e18 / growthDecay;
            interest = _totalPaidDebt * (currBorrowRate - _lastRate) / _expRate / 365 days;
        } else if (_lastFreeDebtRatioBps > _targetFreeDebtRatioEndBps) {
            currBorrowRate = _lastRate * growthDecay / 1e18;
            if (currBorrowRate < MIN_RATE) {
                currBorrowRate = MIN_RATE;
                // calculate integral
                if (_lastRate <= MIN_RATE) {
                    // Already at min rate, just use flat rate for entire period
                    interest = _totalPaidDebt * MIN_RATE * _timeElapsed / 365 days / 1e18;
                } else {
                    uint timeToMin = uint(-wadLn(int(MIN_RATE * 1e18 / _lastRate))) / _expRate;
                    // Decaying integral up to min rate, then add flat rate portion
                    interest = _totalPaidDebt * ((_lastRate - MIN_RATE) / _expRate + 
                              MIN_RATE * (_timeElapsed - timeToMin)) / 365 days / 1e18;
                }
            } else {
                interest = _totalPaidDebt * (_lastRate - currBorrowRate) / _expRate / 365 days;
            }
        } else {
            currBorrowRate = _lastRate;
            interest = _totalPaidDebt * _lastRate * _timeElapsed / 365 days / 1e18;
        }
    }
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ERC20} from "../tokens/ERC20.sol";
import {SafeTransferLib} from "../utils/SafeTransferLib.sol";
import {FixedPointMathLib} from "../utils/FixedPointMathLib.sol";

/// @notice Minimal ERC4626 tokenized Vault implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC4626.sol)
abstract contract ERC4626 is ERC20 {
    using SafeTransferLib for ERC20;
    using FixedPointMathLib for uint256;

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

    event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);

    event Withdraw(
        address indexed caller,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    /*//////////////////////////////////////////////////////////////
                               IMMUTABLES
    //////////////////////////////////////////////////////////////*/

    ERC20 public immutable asset;

    constructor(
        ERC20 _asset,
        string memory _name,
        string memory _symbol
    ) ERC20(_name, _symbol, _asset.decimals()) {
        asset = _asset;
    }

    /*//////////////////////////////////////////////////////////////
                        DEPOSIT/WITHDRAWAL LOGIC
    //////////////////////////////////////////////////////////////*/

    function deposit(uint256 assets, address receiver) public virtual returns (uint256 shares) {
        // Check for rounding error since we round down in previewDeposit.
        require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES");

        // Need to transfer before minting or ERC777s could reenter.
        asset.safeTransferFrom(msg.sender, address(this), assets);

        _mint(receiver, shares);

        emit Deposit(msg.sender, receiver, assets, shares);

        afterDeposit(assets, shares);
    }

    function mint(uint256 shares, address receiver) public virtual returns (uint256 assets) {
        assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up.

        // Need to transfer before minting or ERC777s could reenter.
        asset.safeTransferFrom(msg.sender, address(this), assets);

        _mint(receiver, shares);

        emit Deposit(msg.sender, receiver, assets, shares);

        afterDeposit(assets, shares);
    }

    function withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) public virtual returns (uint256 shares) {
        shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up.

        if (msg.sender != owner) {
            uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.

            if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
        }

        beforeWithdraw(assets, shares);

        _burn(owner, shares);

        emit Withdraw(msg.sender, receiver, owner, assets, shares);

        asset.safeTransfer(receiver, assets);
    }

    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) public virtual returns (uint256 assets) {
        if (msg.sender != owner) {
            uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.

            if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
        }

        // Check for rounding error since we round down in previewRedeem.
        require((assets = previewRedeem(shares)) != 0, "ZERO_ASSETS");

        beforeWithdraw(assets, shares);

        _burn(owner, shares);

        emit Withdraw(msg.sender, receiver, owner, assets, shares);

        asset.safeTransfer(receiver, assets);
    }

    /*//////////////////////////////////////////////////////////////
                            ACCOUNTING LOGIC
    //////////////////////////////////////////////////////////////*/

    function totalAssets() public view virtual returns (uint256);

    function convertToShares(uint256 assets) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets());
    }

    function convertToAssets(uint256 shares) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? shares : shares.mulDivDown(totalAssets(), supply);
    }

    function previewDeposit(uint256 assets) public view virtual returns (uint256) {
        return convertToShares(assets);
    }

    function previewMint(uint256 shares) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? shares : shares.mulDivUp(totalAssets(), supply);
    }

    function previewWithdraw(uint256 assets) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? assets : assets.mulDivUp(supply, totalAssets());
    }

    function previewRedeem(uint256 shares) public view virtual returns (uint256) {
        return convertToAssets(shares);
    }

    /*//////////////////////////////////////////////////////////////
                     DEPOSIT/WITHDRAWAL LIMIT LOGIC
    //////////////////////////////////////////////////////////////*/

    function maxDeposit(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    function maxMint(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    function maxWithdraw(address owner) public view virtual returns (uint256) {
        return convertToAssets(balanceOf[owner]);
    }

    function maxRedeem(address owner) public view virtual returns (uint256) {
        return balanceOf[owner];
    }

    /*//////////////////////////////////////////////////////////////
                          INTERNAL HOOKS LOGIC
    //////////////////////////////////////////////////////////////*/

    function beforeWithdraw(uint256 assets, uint256 shares) internal virtual {}

    function afterDeposit(uint256 assets, uint256 shares) internal virtual {}
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

/// @notice Signed 18 decimal fixed point (wad) arithmetic library.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SignedWadMath.sol)
/// @author Modified from Remco Bloemen (https://xn--2-umb.com/22/exp-ln/index.html)

/// @dev Will not revert on overflow, only use where overflow is not possible.
function toWadUnsafe(uint256 x) pure returns (int256 r) {
    /// @solidity memory-safe-assembly
    assembly {
        // Multiply x by 1e18.
        r := mul(x, 1000000000000000000)
    }
}

/// @dev Takes an integer amount of seconds and converts it to a wad amount of days.
/// @dev Will not revert on overflow, only use where overflow is not possible.
/// @dev Not meant for negative second amounts, it assumes x is positive.
function toDaysWadUnsafe(uint256 x) pure returns (int256 r) {
    /// @solidity memory-safe-assembly
    assembly {
        // Multiply x by 1e18 and then divide it by 86400.
        r := div(mul(x, 1000000000000000000), 86400)
    }
}

/// @dev Takes a wad amount of days and converts it to an integer amount of seconds.
/// @dev Will not revert on overflow, only use where overflow is not possible.
/// @dev Not meant for negative day amounts, it assumes x is positive.
function fromDaysWadUnsafe(int256 x) pure returns (uint256 r) {
    /// @solidity memory-safe-assembly
    assembly {
        // Multiply x by 86400 and then divide it by 1e18.
        r := div(mul(x, 86400), 1000000000000000000)
    }
}

/// @dev Will not revert on overflow, only use where overflow is not possible.
function unsafeWadMul(int256 x, int256 y) pure returns (int256 r) {
    /// @solidity memory-safe-assembly
    assembly {
        // Multiply x by y and divide by 1e18.
        r := sdiv(mul(x, y), 1000000000000000000)
    }
}

/// @dev Will return 0 instead of reverting if y is zero and will
/// not revert on overflow, only use where overflow is not possible.
function unsafeWadDiv(int256 x, int256 y) pure returns (int256 r) {
    /// @solidity memory-safe-assembly
    assembly {
        // Multiply x by 1e18 and divide it by y.
        r := sdiv(mul(x, 1000000000000000000), y)
    }
}

function wadMul(int256 x, int256 y) pure returns (int256 r) {
    /// @solidity memory-safe-assembly
    assembly {
        // Store x * y in r for now.
        r := mul(x, y)

        // Combined overflow check (`x == 0 || (x * y) / x == y`) and edge case check
        // where x == -1 and y == type(int256).min, for y == -1 and x == min int256,
        // the second overflow check will catch this.
        // See: https://secure-contracts.com/learn_evm/arithmetic-checks.html#arithmetic-checks-for-int256-multiplication
        // Combining into 1 expression saves gas as resulting bytecode will only have 1 `JUMPI`
        // rather than 2.
        if iszero(
            and(
                or(iszero(x), eq(sdiv(r, x), y)),
                or(lt(x, not(0)), sgt(y, 0x8000000000000000000000000000000000000000000000000000000000000000))
            )
        ) {
            revert(0, 0)
        }

        // Scale the result down by 1e18.
        r := sdiv(r, 1000000000000000000)
    }
}

function wadDiv(int256 x, int256 y) pure returns (int256 r) {
    /// @solidity memory-safe-assembly
    assembly {
        // Store x * 1e18 in r for now.
        r := mul(x, 1000000000000000000)

        // Equivalent to require(y != 0 && ((x * 1e18) / 1e18 == x))
        if iszero(and(iszero(iszero(y)), eq(sdiv(r, 1000000000000000000), x))) {
            revert(0, 0)
        }

        // Divide r by y.
        r := sdiv(r, y)
    }
}

/// @dev Will not work with negative bases, only use when x is positive.
function wadPow(int256 x, int256 y) pure returns (int256) {
    // Equivalent to x to the power of y because x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)
    return wadExp((wadLn(x) * y) / 1e18); // Using ln(x) means x must be greater than 0.
}

function wadExp(int256 x) pure returns (int256 r) {
    unchecked {
        // When the result is < 0.5 we return zero. This happens when
        // x <= floor(log(0.5e18) * 1e18) ~ -42e18
        if (x <= -42139678854452767551) return 0;

        // When the result is > (2**255 - 1) / 1e18 we can not represent it as an
        // int. This happens when x >= floor(log((2**255 - 1) / 1e18) * 1e18) ~ 135.
        if (x >= 135305999368893231589) revert("EXP_OVERFLOW");

        // x is now in the range (-42, 136) * 1e18. Convert to (-42, 136) * 2**96
        // for more intermediate precision and a binary basis. This base conversion
        // is a multiplication by 1e18 / 2**96 = 5**18 / 2**78.
        x = (x << 78) / 5**18;

        // Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers
        // of two such that exp(x) = exp(x') * 2**k, where k is an integer.
        // Solving this gives k = round(x / log(2)) and x' = x - k * log(2).
        int256 k = ((x << 96) / 54916777467707473351141471128 + 2**95) >> 96;
        x = x - k * 54916777467707473351141471128;

        // k is in the range [-61, 195].

        // Evaluate using a (6, 7)-term rational approximation.
        // p is made monic, we'll multiply by a scale factor later.
        int256 y = x + 1346386616545796478920950773328;
        y = ((y * x) >> 96) + 57155421227552351082224309758442;
        int256 p = y + x - 94201549194550492254356042504812;
        p = ((p * y) >> 96) + 28719021644029726153956944680412240;
        p = p * x + (4385272521454847904659076985693276 << 96);

        // We leave p in 2**192 basis so we don't need to scale it back up for the division.
        int256 q = x - 2855989394907223263936484059900;
        q = ((q * x) >> 96) + 50020603652535783019961831881945;
        q = ((q * x) >> 96) - 533845033583426703283633433725380;
        q = ((q * x) >> 96) + 3604857256930695427073651918091429;
        q = ((q * x) >> 96) - 14423608567350463180887372962807573;
        q = ((q * x) >> 96) + 26449188498355588339934803723976023;

        /// @solidity memory-safe-assembly
        assembly {
            // Div in assembly because solidity adds a zero check despite the unchecked.
            // The q polynomial won't have zeros in the domain as all its roots are complex.
            // No scaling is necessary because p is already 2**96 too large.
            r := sdiv(p, q)
        }

        // r should be in the range (0.09, 0.25) * 2**96.

        // We now need to multiply r by:
        // * the scale factor s = ~6.031367120.
        // * the 2**k factor from the range reduction.
        // * the 1e18 / 2**96 factor for base conversion.
        // We do this all at once, with an intermediate result in 2**213
        // basis, so the final right shift is always by a positive amount.
        r = int256((uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k));
    }
}

function wadLn(int256 x) pure returns (int256 r) {
    unchecked {
        require(x > 0, "UNDEFINED");

        // We want to convert x from 10**18 fixed point to 2**96 fixed point.
        // We do this by multiplying by 2**96 / 10**18. But since
        // ln(x * C) = ln(x) + ln(C), we can simply do nothing here
        // and add ln(2**96 / 10**18) at the end.

        /// @solidity memory-safe-assembly
        assembly {
            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            r := or(r, shl(2, lt(0xf, shr(r, x))))
            r := or(r, shl(1, lt(0x3, shr(r, x))))
            r := or(r, lt(0x1, shr(r, x)))
        }

        // Reduce range of x to (1, 2) * 2**96
        // ln(2^k * x) = k * ln(2) + ln(x)
        int256 k = r - 96;
        x <<= uint256(159 - k);
        x = int256(uint256(x) >> 159);

        // Evaluate using a (8, 8)-term rational approximation.
        // p is made monic, we will multiply by a scale factor later.
        int256 p = x + 3273285459638523848632254066296;
        p = ((p * x) >> 96) + 24828157081833163892658089445524;
        p = ((p * x) >> 96) + 43456485725739037958740375743393;
        p = ((p * x) >> 96) - 11111509109440967052023855526967;
        p = ((p * x) >> 96) - 45023709667254063763336534515857;
        p = ((p * x) >> 96) - 14706773417378608786704636184526;
        p = p * x - (795164235651350426258249787498 << 96);

        // We leave p in 2**192 basis so we don't need to scale it back up for the division.
        // q is monic by convention.
        int256 q = x + 5573035233440673466300451813936;
        q = ((q * x) >> 96) + 71694874799317883764090561454958;
        q = ((q * x) >> 96) + 283447036172924575727196451306956;
        q = ((q * x) >> 96) + 401686690394027663651624208769553;
        q = ((q * x) >> 96) + 204048457590392012362485061816622;
        q = ((q * x) >> 96) + 31853899698501571402653359427138;
        q = ((q * x) >> 96) + 909429971244387300277376558375;
        /// @solidity memory-safe-assembly
        assembly {
            // Div in assembly because solidity adds a zero check despite the unchecked.
            // The q polynomial is known not to have zeros in the domain.
            // No scaling required because p is already 2**96 too large.
            r := sdiv(p, q)
        }

        // r is in the range (0, 0.125) * 2**96

        // Finalization, we need to:
        // * multiply by the scale factor s = 5.549…
        // * add ln(2**96 / 10**18)
        // * add k * ln(2)
        // * multiply by 10**18 / 2**96 = 5**18 >> 78

        // mul s * 5e18 * 2**96, base is now 5**18 * 2**192
        r *= 1677202110996718588342820967067443963516166;
        // add ln(2) * k * 5e18 * 2**192
        r += 16597577552685614221487285958193947469193820559219878177908093499208371 * k;
        // add ln(2**96 / 10**18) * 5e18 * 2**192
        r += 600920179829731861736702779321621459595472258049074101567377883020018308;
        // base conversion: mul 2**18 / 2**192
        r >>= 174;
    }
}

/// @dev Will return 0 instead of reverting if y is zero.
function unsafeDiv(int256 x, int256 y) pure returns (int256 r) {
    /// @solidity memory-safe-assembly
    assembly {
        // Divide x by y.
        r := sdiv(x, y)
    }
}

Settings
{
  "remappings": [
    "ds-test/=lib/solmate/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "solmate/=lib/solmate/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "viaIR": false
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"contract ERC20","name":"_collateral","type":"address"},{"internalType":"contract IChainlinkFeed","name":"_feed","type":"address"},{"internalType":"contract Coin","name":"_coin","type":"address"},{"internalType":"contract Vault","name":"_vault","type":"address"},{"internalType":"contract InterestModel","name":"_interestModel","type":"address"},{"internalType":"contract IFactory","name":"_factory","type":"address"},{"internalType":"address","name":"_operator","type":"address"},{"internalType":"uint256","name":"_collateralFactor","type":"uint256"},{"internalType":"uint256","name":"_minDebt","type":"uint256"},{"internalType":"uint256","name":"_timeUntilImmutability","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"address","name":"delegatee","type":"address"},{"indexed":false,"internalType":"bool","name":"isDelegatee","type":"bool"}],"name":"DelegationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"halfLife","type":"uint64"}],"name":"HalfLifeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":true,"internalType":"address","name":"liquidator","type":"address"},{"indexed":false,"internalType":"uint256","name":"repayAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateralOut","type":"uint256"}],"name":"Liquidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"feeBps","type":"uint256"}],"name":"LocalReserveFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"NewEpoch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"}],"name":"OperatorAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOperator","type":"address"}],"name":"PendingOperatorUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"int256","name":"collateralDelta","type":"int256"},{"indexed":false,"internalType":"int256","name":"debtDelta","type":"int256"}],"name":"PositionAdjusted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"redeemFeeBps","type":"uint16"}],"name":"RedeemFeeBpsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"}],"name":"Redeemed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"bool","name":"isRedeemable","type":"bool"}],"name":"RedemptionStatusUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"startBps","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"endBps","type":"uint16"}],"name":"TargetFreeDebtRatioUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"debt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateral","type":"uint256"}],"name":"WrittenOff","type":"event"},{"inputs":[],"name":"MIN_LIQUIDATION_DEBT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"STALENESS_THRESHOLD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"STALENESS_UNWIND_DURATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"_cachedCollateralBalances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"accrueInterest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"accruedGlobalReserves","outputs":[{"internalType":"uint120","name":"","type":"uint120"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accruedLocalReserves","outputs":[{"internalType":"uint120","name":"","type":"uint120"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"int256","name":"collateralDelta","type":"int256"},{"internalType":"int256","name":"debtDelta","type":"int256"}],"name":"adjust","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"int256","name":"collateralDelta","type":"int256"},{"internalType":"int256","name":"debtDelta","type":"int256"},{"internalType":"bool","name":"chooseRedeemable","type":"bool"}],"name":"adjust","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"borrowerEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"borrowerLastRedeemedIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cachedGlobalFeeBps","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"coin","outputs":[{"internalType":"contract Coin","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collateral","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collateralFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"},{"internalType":"bool","name":"isDelegatee","type":"bool"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"delegations","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"enableImmutabilityNow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"epoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"epochRedeemedCollateral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"expRate","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"contract IFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeBps","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feed","outputs":[{"internalType":"contract IChainlinkFeed","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"freeDebtShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCollateralPrice","outputs":[{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"bool","name":"reduceOnly","type":"bool"},{"internalType":"bool","name":"allowLiquidations","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getDebtOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFeedPrice","outputs":[{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFreeDebtRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"name":"getRedeemAmountOut","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"immutabilityDeadline","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"interestModel","outputs":[{"internalType":"contract InterestModel","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isRedeemable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastAccrue","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastBorrowRateMantissa","outputs":[{"internalType":"uint88","name":"","type":"uint88"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"repayAmount","type":"uint256"},{"internalType":"uint256","name":"minCollateralOut","type":"uint256"}],"name":"liquidate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"minDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nonRedeemableCollateral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"operator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"paidDebtShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOperator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"}],"name":"pullGlobalReserves","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pullLocalReserves","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"redeemFeeBps","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"halfLife","type":"uint64"}],"name":"setHalfLife","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_feeBps","type":"uint256"}],"name":"setLocalReserveFeeBps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_pendingOperator","type":"address"}],"name":"setPendingOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_redeemFeeBps","type":"uint16"}],"name":"setRedeemFeeBps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bool","name":"chooseRedeemable","type":"bool"}],"name":"setRedemptionStatus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"startBps","type":"uint16"},{"internalType":"uint16","name":"endBps","type":"uint16"}],"name":"setTargetFreeDebtRatio","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"targetFreeDebtRatioEndBps","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"targetFreeDebtRatioStartBps","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalFreeDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalFreeDebtShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalPaidDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalPaidDebtShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"contract Vault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"writeOff","outputs":[{"internalType":"bool","name":"writtenOff","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]

0x6101806040526000805465ffffffffffff1916641e0fa007d017905562093a8062000032671bc16d674ec800006200029f565b6200003e9190620004c8565b600080547fffff0000000000000000000000ffffffffff0000000000000000ffffffffffff1666010000000000006001600160401b039390931692909202600160981b600160f01b03191691909117642386f26fc160a71b179055348015620000a657600080fd5b506040516200464f3803806200464f833981016040819052620000c99162000504565b612710831115620001215760405162461bcd60e51b815260206004820152601960248201527f496e76616c696420636f6c6c61746572616c20666163746f720000000000000060448201526064015b60405180910390fd5b630784ce008110620001865760405162461bcd60e51b815260206004820152602760248201527f4d617820696d6d75746162696c69747920646561646c696e6520697320696e206044820152663420796561727360c81b606482015260840162000118565b6001600160a01b038a811660a05289811660c05288811660805287811660e0528681166101005285811661012052600280546001600160a01b031916918616919091179055610140839052610160829052620001e38142620005ce565b60049081556000805464ffffffffff60701b1916600160701b4264ffffffffff16021790556101205160405163dfd56cff60e01b815230928101929092526001600160a01b03169063dfd56cff90602401602060405180830381865afa15801562000252573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002789190620005f5565b6001805461ffff191661ffff92909216919091179055506200060f98505050505050505050565b6000808213620002de5760405162461bcd60e51b815260206004820152600960248201526815539111519253915160ba1b604482015260640162000118565b5060606001600160801b03821160071b82811c6001600160401b031060061b1782811c63ffffffff1060051b1782811c61ffff1060041b1782811c60ff10600390811b90911783811c600f1060021b1783811c909110600190811b90911783811c90911017609f81810383019390931b90921c6c465772b2bbbb5f824b15207a3081018102821d6d0388eaa27412d5aca026815d636e018102821d6d0df99ac502031bf953eff472fdcc018102821d6d13cdffb29d51d99322bdff5f2211018102821d6d0a0f742023def783a307a986912e018102821d6d01920d8043ca89b5239253284e42018102821d6c0b7a86d7375468fac667a0a527016c29508e458543d8aa4df2abee7882018202831d6d0139601a2efabe717e604cbb4894018202831d6d02247f7a7b6594320649aa03aba1018202831d6c8c3f38e95a6b1ff2ab1c3b343619018202831d6d02384773bdf1ac5676facced60901901820290921d6cb9a025d814b29c212b8b1a07cd19010260016c0504a838426634cdd8738f543560611b03190105711340daa0d5f769dba1915cef59f0815a550602605f19919091017d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b302017d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b88642840160ae1d90565b600082620004e657634e487b7160e01b600052601260045260246000fd5b500490565b6001600160a01b03811681146200050157600080fd5b50565b6000806000806000806000806000806101408b8d0312156200052557600080fd5b8a516200053281620004eb565b60208c0151909a506200054581620004eb565b60408c01519099506200055881620004eb565b60608c01519098506200056b81620004eb565b60808c01519097506200057e81620004eb565b60a08c01519096506200059181620004eb565b60c08c0151909550620005a481620004eb565b8094505060e08b015192506101008b015191506101208b015190509295989b9194979a5092959850565b60008219821115620005f057634e487b7160e01b600052601160045260246000fd5b500190565b6000602082840312156200060857600080fd5b5051919050565b60805160a05160c05160e05161010051610120516101405161016051613f016200074e600039600081816109000152611a840152600081816108b901528181611c390152818161313801526133c00152600081816107eb015281816124b10152612bcc01526000818161077601526126f701526000818161095501528181612904015281816129c10152612ac701526000818161059f01528181611cdc0152611d6701526000818161086a01528181610bb901528181610ffb015281816116c30152818161173b0152818161184901528181611ded0152818161219701526123ae01526000818161046a01528181610c5801528181610cdc015281816118a00152818161197401528181611a000152818161226b015281816122f70152818161252e015281816129f301528181612af60152612cfe0152613f016000f3fe608060405234801561001057600080fd5b50600436106103a45760003560e01c806376db6c8d116101e9578063c64814dd1161010f578063df011c41116100ad578063fb529d331161007c578063fb529d3314610947578063fbfa77cf14610950578063fcad444814610977578063fe55da611461097f57600080fd5b8063df011c41146108b4578063df4fe5fa146108db578063f17336d7146108fb578063f7683bbc1461092257600080fd5b8063d854fd26116100e9578063d854fd261461084c578063d8dfeb4514610865578063d989c69c1461088c578063dba6bf1a1461089457600080fd5b8063c64814dd1461080d578063ca36b6961461083b578063cb120c3a1461084357600080fd5b8063a3a0915f11610187578063ac7e534e11610156578063ac7e534e14610798578063b56038dc146107ab578063b5e007a8146107dd578063c45a0155146107e657600080fd5b8063a3a0915f14610736578063a553762014610756578063a6afed9514610769578063ac165d7a1461077157600080fd5b8063900cf0cf116101c3578063900cf0cf146106f6578063905fa73d146106ff578063950c801014610712578063982ef0a71461072357600080fd5b806376db6c8d146106ad5780637cbc2373146106d05780638687803d146106e357600080fd5b80632f0a454e116102ce5780634b233bfa1161026c5780636682acdc1161023b5780636682acdc1461068857806371e35e84146106925780637215b0bf1461069a57806374036385146106a457600080fd5b80634b233bfa1461063957806355ac039514610642578063570ca735146106625780635a99253b1461067557600080fd5b806339cdab5c116102a857806339cdab5c146105c15780633b19f2d2146105de578063415d0f35146105f2578063483389e41461060557600080fd5b80632f0a454e146105675780632fc18d271461057a57806337a7b7d81461059a57600080fd5b8063143d4e4911610346578063220195411161031557806322019541146104fa57806322ff2a8e1461050d57806324a9d853146105205780632a87d11c1461053557600080fd5b8063143d4e49146104a4578063158b1a8b146104b957806315cf8d17146104d9578063179cac9f146104e757600080fd5b806309f6442c1161038257806309f6442c146103eb5780630b1ae707146104145780630c81eb231461044257806311df99951461046557600080fd5b806305c7bfc1146103a95780630710285c146103c5578063077e5fe3146103d8575b600080fd5b6103b260125481565b6040519081526020015b60405180910390f35b6103b26103d3366004613945565b61098d565b6103b26103e6366004613978565b610e16565b60005461040190640100000000900461ffff1681565b60405161ffff90911681526020016103bc565b60005461042c90600160701b900464ffffffffff1681565b60405164ffffffffff90911681526020016103bc565b610455610450366004613991565b610ea4565b60405190151581526020016103bc565b61048c7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016103bc565b6104b76104b23660046139c4565b6110c6565b005b6103b26104c73660046139c4565b600a6020526000908152604090205481565b6001546104019061ffff1681565b6104b76104f53660046139ed565b61113a565b6104b7610508366004613a36565b611383565b6104b761051b366004613a60565b6114f1565b60005461040190600160f01b900461ffff1681565b60015461054f90600160881b90046001600160781b031681565b6040516001600160781b0390911681526020016103bc565b6104b7610575366004613945565b611636565b6103b26105883660046139c4565b600f6020526000908152604090205481565b61048c7f000000000000000000000000000000000000000000000000000000000000000081565b6105c9611cd4565b604080519283526020830191909152016103bc565b6000546104019062010000900461ffff1681565b6104b7610600366004613978565b611f16565b60005461062090600160301b900467ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020016103bc565b6103b260045481565b6103b2610650366004613978565b60116020526000908152604090205481565b60025461048c906001600160a01b031681565b6104b7610683366004613a8a565b611fd3565b6103b26201518081565b6104b76120c7565b6103b262015f9081565b6103b260085481565b6104556106bb3660046139c4565b600d6020526000908152604090205460ff1681565b6103b26106de366004613aa5565b612118565b6104b76106f13660046139c4565b6124a6565b6103b260095481565b6104b761070d366004613ac7565b6125a8565b6103b269021e19e0c9bab240000081565b6104b76107313660046139ed565b6125c3565b6103b26107443660046139c4565b60106020526000908152604090205481565b6103b26107643660046139c4565b61262f565b6104b76126c6565b61048c7f000000000000000000000000000000000000000000000000000000000000000081565b60035461048c906001600160a01b031681565b6000546107c590600160981b90046001600160581b031681565b6040516001600160581b0390911681526020016103bc565b6103b260065481565b61048c7f000000000000000000000000000000000000000000000000000000000000000081565b61045561081b366004613991565b600e60209081526000928352604080842090915290825290205460ff1681565b6103b2612c5f565b6103b260055481565b60015461054f906201000090046001600160781b031681565b61048c7f000000000000000000000000000000000000000000000000000000000000000081565b6104b7612c9f565b6103b26108a23660046139c4565b600b6020526000908152604090205481565b6103b27f000000000000000000000000000000000000000000000000000000000000000081565b6103b26108e93660046139c4565b600c6020526000908152604090205481565b6103b27f000000000000000000000000000000000000000000000000000000000000000081565b61092a612d7e565b6040805193845291151560208401521515908201526060016103bc565b6103b260075481565b61048c7f000000000000000000000000000000000000000000000000000000000000000081565b6104b7612e9e565b6000546104019061ffff1681565b60006109976126c6565b6109a084612f14565b60008311610a015760405162461bcd60e51b815260206004820152602360248201527f526570617920616d6f756e74206d75737420626520677265617465722074686160448201526206e20360ec1b60648201526084015b60405180910390fd5b600080610a0c612d7e565b925050915080610a565760405162461bcd60e51b81526020600482015260156024820152741b1a5c5d5a59185d1a5bdb9cc8191a5cd8589b1959605a1b60448201526064016109f8565b6000610a618761262f565b6001600160a01b0388166000908152600a6020526040812054919250610a88828685613127565b905060008111610ada5760405162461bcd60e51b815260206004820152601e60248201527f696e73756666696369656e74206c6971756964617461626c652064656274000060448201526064016109f8565b80881115610ae6578097505b610af089896131e0565b6000610afd83878661336f565b90506000612710610b0e8382613b25565b610b18908c613b3d565b610b229190613b5c565b9050600087610b3983670de0b6b3a7640000613b3d565b610b439190613b5c565b9050848111610b525780610b54565b845b905089811015610ba65760405162461bcd60e51b815260206004820152601b60248201527f696e73756666696369656e7420636f6c6c61746572616c206f7574000000000060448201526064016109f8565b8015610c2e57610be06001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163383613458565b610bea8186613b7e565b6001600160a01b038d166000908152600a6020908152604080832093909355600d9052205460ff16610c2e578060126000828254610c289190613b7e565b90915550505b6040516323b872dd60e01b8152336004820152306024820152604481018c90526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906323b872dd906064016020604051808303816000875af1158015610ca1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cc59190613b95565b50604051630852cd8d60e31b8152600481018c90527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906342966c6890602401600060405180830381600087803b158015610d2857600080fd5b505af1158015610d3c573d6000803e3d6000fd5b50505050336001600160a01b03168c6001600160a01b03167f1f0c6615429d1cdae0dfa233abf91d3b31cdbdd82c8081389832a61e1072f1ea8d84604051610d8e929190918252602082015260400190565b60405180910390a3604051630c81eb2360e01b81526001600160a01b038d1660048201523360248201523090630c81eb23906044016020604051808303816000875af1925050508015610dfe575060408051601f3d908101601f19168201909252610dfb91810190613b95565b60015b15610e0557505b9750505050505050505b9392505050565b6000600554821115610e2a57506000919050565b600080610e35612d7e565b925050915080610e49575060009392505050565b600054612710908390610e6890640100000000900461ffff1683613bb2565b61ffff16610e7e87670de0b6b3a7640000613b3d565b610e889190613b3d565b610e929190613b5c565b610e9c9190613b5c565b949350505050565b6000610eae6126c6565b610eb783612f14565b6000610ec28461262f565b905080156110bf576001600160a01b0384166000908152600a60205260408120549080610eed612d7e565b925050915080610f375760405162461bcd60e51b81526020600482015260156024820152741b1a5c5d5a59185d1a5bdb9cc8191a5cd8589b1959605a1b60448201526064016109f8565b6000670de0b6b3a7640000610f4c8585613b3d565b610f569190613b5c565b9050610f63816064613b3d565b8511156110ba57610f76886000196131e0565b6000600754600554610f889190613b25565b90508015610fee5760008160055488610fa19190613b3d565b610fab9190613b5c565b90506000610fb98289613b7e565b90508160056000828254610fcd9190613b25565b925050819055508060076000828254610fe69190613b25565b909155505050505b6110226001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168987613458565b6001600160a01b0389166000908152600d602052604090205460ff1661105a5784601260008282546110549190613b7e565b90915550505b6001600160a01b038981166000818152600a602090815260408083209290925581518a8152908101899052928b16927f10fd981a899851c05d0ff91c65c8eedbdde2dec8306e05bc4643dcba83fa2e10910160405180910390a360019650505b505050505b5092915050565b6002546001600160a01b031633146110f05760405162461bcd60e51b81526004016109f890613bd5565b600380546001600160a01b0319166001600160a01b0383169081179091556040517fc1888917e06295ab8d09c160eaaf1b4c5c251f3f2a05442fea8ad79cd43d671a90600090a250565b6111426126c6565b61114b82612f14565b336001600160a01b038316148061118557506001600160a01b0382166000908152600e6020908152604080832033845290915290205460ff165b6111a15760405162461bcd60e51b81526004016109f890613bd5565b6001600160a01b0382166000908152600d602052604090205460ff161515811515036111cb575050565b8015611232576009546001600160a01b03831660008181526010602090815260408083208590559382526011815283822054928252600f815283822092909255600a909152908120546012805491929091611227908490613b7e565b909155506112649050565b6001600160a01b0382166000908152600a6020526040812054601280549192909161125e908490613b25565b90915550505b600061126f8361262f565b9050801561131457611283836000196131e0565b6001600160a01b0383166000908152600d60205260409020805460ff19168315151790556112b183826134d9565b60006112bc8461262f565b90508181101561130e5760405162461bcd60e51b815260206004820152601b60248201527f446562742064656372656173656420756e65787065637465646c79000000000060448201526064016109f8565b50611339565b6001600160a01b0383166000908152600d60205260409020805460ff19168315151790555b826001600160a01b03167f79557fc8b078a56ef50a6155bd33758b99bfa61d29f74151f4b8aa575a3ecb1083604051611376911515815260200190565b60405180910390a2505050565b6002546001600160a01b031633146113ad5760405162461bcd60e51b81526004016109f890613bd5565b60045442106113ce5760405162461bcd60e51b81526004016109f890613bfb565b6113d66126c6565b6101f48261ffff16101580156113f457508061ffff168261ffff1611155b6114345760405162461bcd60e51b8152602060048201526011602482015270496e76616c69642073746172742062707360781b60448201526064016109f8565b8161ffff168161ffff1610158015611452575061251c8161ffff1611155b6114905760405162461bcd60e51b815260206004820152600f60248201526e496e76616c696420656e642062707360881b60448201526064016109f8565b6000805461ffff84811663ffffffff19909216821762010000918516918202179092556040805191825260208201929092527fde11d0b14fa526d6653cddfb3426cc250f98f6e72a6a36fdae6c33d85df770d1910160405180910390a15050565b6002546001600160a01b0316331461151b5760405162461bcd60e51b81526004016109f890613bd5565b600454421061153c5760405162461bcd60e51b81526004016109f890613bfb565b6115446126c6565b61a8c08167ffffffffffffffff161015801561156d575062278d008167ffffffffffffffff1611155b6115ad5760405162461bcd60e51b8152602060048201526011602482015270496e76616c69642068616c66206c69666560781b60448201526064016109f8565b8067ffffffffffffffff166115c9671bc16d674ec80000613602565b6115d39190613b5c565b600080546dffffffffffffffff0000000000001916600160301b67ffffffffffffffff9384160217905560405190821681527fcd505beca76120b741b38fe89f62433d1e15c39460e3755a9e5ca3f1f3ce7a29906020015b60405180910390a150565b61163e6126c6565b61164783612f14565b60008213156116f0576001600160a01b0383166000908152600d602052604090205460ff166116885781601260008282546116829190613b25565b90915550505b6001600160a01b0383166000908152600a6020526040812080548492906116b0908490613b25565b909155506116eb90506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016333085613839565b611870565b6000821215611870576001600160a01b0383166000908152600d602052604090205460ff16156117db5760125461172683613c24565b6040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa15801561178a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117ae9190613c40565b6117b89190613b7e565b10156117d65760405162461bcd60e51b81526004016109f890613c59565b6117fb565b6117e482613c24565b601260008282546117f59190613b7e565b90915550505b61180482613c24565b6001600160a01b0384166000908152600a60205260408120805490919061182c908490613b7e565b9091555061187090503361183f84613c24565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169190613458565b600081131561190a578061188484826134d9565b6040516340c10f1960e01b8152336004820152602481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906340c10f1990604401600060405180830381600087803b1580156118ec57600080fd5b505af1158015611900573d6000803e3d6000fd5b5050505050611a67565b6000811215611a6757600061191e82613c24565b9050600061192b8561262f565b905081811161194857809150611943856000196131e0565b611952565b61195285836131e0565b6040516323b872dd60e01b8152336004820152306024820152604481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906323b872dd906064016020604051808303816000875af11580156119c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119e99190613b95565b50604051630852cd8d60e31b8152600481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906342966c6890602401600060405180830381600087803b158015611a4c57600080fd5b505af1158015611a60573d6000803e3d6000fd5b5050505050505b6000611a728461262f565b90508115611aff57801580611aa757507f00000000000000000000000000000000000000000000000000000000000000008110155b611aff5760405162461bcd60e51b8152602060048201526024808201527f446562742062656c6f77206d696e696d756d20616e64206c61726765722074686044820152630616e20360e41b60648201526084016109f8565b60408051848152602081018490526001600160a01b038616917f2621f237099759c662179354fad7ce6acb6344513f178385af1c93d9abb81cb1910160405180910390a260008312158015611b55575060008213155b15611b605750505050565b336001600160a01b0385161480611b9a57506001600160a01b0384166000908152600e6020908152604080832033845290915290205460ff165b611bb65760405162461bcd60e51b81526004016109f890613bd5565b80600003611bc45750505050565b600080611bcf612d7e565b50915091508015611c105760405162461bcd60e51b815260206004820152600b60248201526a526564756365206f6e6c7960a81b60448201526064016109f8565b6001600160a01b0386166000908152600a602052604081205461271090670de0b6b3a7640000907f000000000000000000000000000000000000000000000000000000000000000090611c639087613b3d565b611c6d9190613b3d565b611c779190613b5c565b611c819190613b5c565b905080841115611ccb5760405162461bcd60e51b815260206004820152601560248201527414dbdb1d995b98de4818da1958dac819985a5b1959605a1b60448201526064016109f8565b50505050505050565b6000806000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015611d38573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d5c9190613cb5565b5093505092505060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015611dc3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611de79190613d05565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e49573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e6d9190613d05565b90506024611e7b8284613d28565b60ff1611611ec957600082611e91836024613d4d565b611e9b9190613d4d565b905060008513611eac576000611ec1565b611eb781600a613e54565b611ec19086613b3d565b965050611f0b565b60006024611ed78385613d28565b611ee19190613d4d565b905060008513611ef2576000611f07565b611efd81600a613e54565b611f079086613b5c565b9650505b829450505050509091565b6002546001600160a01b03163314611f405760405162461bcd60e51b81526004016109f890613bd5565b611f486126c6565b6103e8811115611f885760405162461bcd60e51b815260206004820152600b60248201526a496e76616c69642066656560a81b60448201526064016109f8565b600080546001600160f01b0316600160f01b61ffff8416021790556040518181527f82f90ad483dce76534def3d808c0cb2de1d2d0cf1a52d5421d8347a8e67bd3859060200161162b565b6002546001600160a01b03163314611ffd5760405162461bcd60e51b81526004016109f890613bd5565b600454421061201e5760405162461bcd60e51b81526004016109f890613bfb565b6120266126c6565b61012c8161ffff1611156120755760405162461bcd60e51b8152602060048201526016602482015275496e76616c69642072656465656d206665652062707360501b60448201526064016109f8565b6000805465ffff00000000191664010000000061ffff8416908102919091179091556040519081527f29a575057f3ecc51f2f82c3dd11eabfd86c3a1293ce079baac1ca2cc8a4346f89060200161162b565b6002546001600160a01b031633146120f15760405162461bcd60e51b81526004016109f890613bd5565b60045442106121125760405162461bcd60e51b81526004016109f890613bfb565b42600455565b60006121226126c6565b61212b83610e16565b90508181101561217d5760405162461bcd60e51b815260206004820152601760248201527f696e73756666696369656e7420616d6f756e74206f757400000000000000000060448201526064016109f8565b6012546040516370a0823160e01b815230600482015282907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156121e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061220a9190613c40565b6122149190613b7e565b10156122325760405162461bcd60e51b81526004016109f890613c59565b82600560008282546122449190613b7e565b90915550506040516323b872dd60e01b8152336004820152306024820152604481018490527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906323b872dd906064016020604051808303816000875af11580156122bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122e09190613b95565b50604051630852cd8d60e31b8152600481018490527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906342966c6890602401600060405180830381600087803b15801561234357600080fd5b505af1158015612357573d6000803e3d6000fd5b505060065461237a92508391506ec097ce7bc90715b34b9f1000000000906138d5565b6009546000908152601160205260408120805490919061239b908490613b25565b909155506123d590506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163383613458565b633b9aca006005546006546123ea9190613b5c565b1115612464576009805490600061240083613e63565b909155505060065461242a90670de0b6b3a76400006ec097ce7bc90715b34b9f10000000006138d5565b6006556009546040519081527febad8099c467528a56c98b63c8d476d251cf1ffb4c75db94b4d23fa2b6a1e3359060200160405180910390a15b604080518481526020810183905233917ff3a670cd3af7d64b488926880889d08a8585a138ff455227af6737339a1ec262910160405180910390a25b92915050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146124ee5760405162461bcd60e51b81526004016109f890613bd5565b6124f66126c6565b6001546040516340c10f1960e01b81526001600160a01b038381166004830152600160881b9092046001600160781b031660248201527f0000000000000000000000000000000000000000000000000000000000000000909116906340c10f1990604401600060405180830381600087803b15801561257457600080fd5b505af1158015612588573d6000803e3d6000fd5b50506001805470ffffffffffffffffffffffffffffffffff169055505050565b6125b2848261113a565b6125bd848484611636565b50505050565b336000818152600e602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f5e7ca79ea3ac1b19ddf506e091181a1de49af2216a6e684397d85c0757205336910160405180910390a35050565b6001600160a01b0381166000908152600d602052604081205460ff161561268f5760065415612687576005546006546001600160a01b0384166000908152600b60205260409020546126829290916138d5565b6124a0565b600092915050565b60085415612687576007546008546001600160a01b0384166000908152600c60205260409020546126829290916138d5565b919050565b600080546126e290600160701b900464ffffffffff1642613b7e565b9050806000036126ef5750565b6007546000547f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169163ec95f34591600160981b81046001600160581b0316908590600160301b900467ffffffffffffffff16612752612c5f565b60005460405160e088901b6001600160e01b031916815260048101969096526001600160581b039094166024860152604485019290925267ffffffffffffffff166064840152608483015261ffff80821660a4840152620100009091041660c482015260e4016040805180830381865afa9250505080156127f0575060408051601f3d908101601f191682019092526127ed91810190613e7c565b60015b15612c5c57600080546127109061281290600160f01b900461ffff1684613b3d565b61281c9190613b5c565b600154909150600090612710906128379061ffff1685613b3d565b6128419190613b5c565b905081600160028282829054906101000a90046001600160781b03166128679190613ea0565b92506101000a8154816001600160781b0302191690836001600160781b0316021790555080600160118282829054906101000a90046001600160781b03166128af9190613ea0565b92506101000a8154816001600160781b0302191690836001600160781b031602179055506000816001600160781b0316836001600160781b0316856128f49190613b7e565b6128fe9190613b7e565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa158015612960573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129849190613c40565b9050600754811015612ab0576007546000906129a08385613b3d565b6129aa9190613b5c565b6040516340c10f1960e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018390529192507f0000000000000000000000000000000000000000000000000000000000000000909116906340c10f1990604401600060405180830381600087803b158015612a3957600080fd5b505af1158015612a4d573d6000803e3d6000fd5b5050505060008184612a5f9190613b7e565b905080600160028282829054906101000a90046001600160781b0316612a859190613ea0565b92506101000a8154816001600160781b0302191690836001600160781b031602179055505050612b53565b6040516340c10f1960e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018490527f000000000000000000000000000000000000000000000000000000000000000016906340c10f1990604401600060405180830381600087803b158015612b3a57600080fd5b505af1158015612b4e573d6000803e3d6000fd5b505050505b8460076000828254612b659190613b25565b9091555050600080546fffffffffffffffffffffffffffffffff60701b1916600160701b4264ffffffffff16026affffffffffffffffffffff60981b191617600160981b6001600160581b0389160217905560405163dfd56cff60e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063dfd56cff90602401602060405180830381865afa158015612c1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c3f9190613c40565b6001805461ffff191661ffff929092169190911790555050505050505b50565b6000600554600014612c9957600554600754612c7b9190613b25565b600554612c8a90612710613b3d565b612c949190613b5c565b905090565b50600090565b6002546001600160a01b03163314612cc95760405162461bcd60e51b81526004016109f890613bd5565b612cd16126c6565b6001546040516340c10f1960e01b8152336004820152620100009091046001600160781b031660248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906340c10f1990604401600060405180830381600087803b158015612d4a57600080fd5b505af1158015612d5e573d6000803e3d6000fd5b50506001805470ffffffffffffffffffffffffffffff0000191690555050565b60008060008060019150306001600160a01b03166339cdab5c6040518163ffffffff1660e01b81526004016040805180830381865afa925050508015612de1575060408051601f3d908101601f19168201909252612dde91810190613e7c565b60015b612df2576001925060009150612e0c565b81955080925085600003612e095760019450600093505b50505b42600082821015612e1e576000612e28565b612e288383613b7e565b905062015f90811115612e8557600194506000612e4862015f9083613b7e565b905062015180811015612e7e5762015180612e638282613b7e565b612e6d9089613b3d565b612e779190613b5c565b9650612e83565b600096505b505b8515612e915785612e94565b60015b9550505050909192565b6003546001600160a01b03163314612ec85760405162461bcd60e51b81526004016109f890613bd5565b600354600280546001600160a01b0319166001600160a01b0390921691821790556040517f6317889d8001d27c355e09712356a69d4d49909c262df5cdc3d94df3fc6635b390600090a2565b6001600160a01b0381166000908152600b602052604090205480156130c9576001600160a01b038216600090815260106020908152604080832054600a835281842054600f9093529083205490925b600581108015612f74575060095484105b8015612f805750600085115b1561304157600084815260116020526040812054612f9f908490613b7e565b90506000612fbd82886ec097ce7bc90715b34b9f10000000006138d5565b9050808510612fd557612fd08186613b7e565b612fd8565b60005b9450612fe5600187613b25565b9550613000876ec097ce7bc90715b34b9f10000000006138fb565b60011461302557613020876ec097ce7bc90715b34b9f10000000006138fb565b613028565b60005b96506000935050508061303a90613e63565b9050612f63565b5083156130a057600083815260116020526040812054613062908390613b7e565b9050600061308082876ec097ce7bc90715b34b9f10000000006138d5565b9050808410613098576130938185613b7e565b61309b565b60005b935050505b506001600160a01b0384166000908152600b60209081526040808320869055600a909152902055505b6001600160a01b0382166000908152600d602052604090205460ff1615613123576009546001600160a01b03831660008181526010602090815260408083208590559382526011815283822054928252600f905291909120555b5050565b600080612710670de0b6b3a76400007f00000000000000000000000000000000000000000000000000000000000000006131618888613b3d565b61316b9190613b3d565b6131759190613b5c565b61317f9190613b5c565b905082811115613193576000915050610e0f565b61319e600484613b5c565b915069021e19e0c9bab24000008210156131d85769021e19e0c9bab240000083106131d35769021e19e0c9bab24000006131d5565b825b91505b509392505050565b6001600160a01b0382166000908152600d602052604090205460ff16156132c2576000600019820361323657506001600160a01b0382166000908152600b602052604090205461322f8361262f565b915061324a565b600654600554613247918491613910565b90505b6001600160a01b0383166000908152600b602052604081208054839290613272908490613b7e565b9091555050600654811015613294578060065461328f9190613b7e565b613297565b60005b6006556005548210156132b757816005546132b29190613b7e565b6132ba565b60005b600555505050565b600060001982036132f757506001600160a01b0382166000908152600c60205260409020546132f08361262f565b915061330b565b600854600754613308918491613910565b90505b6001600160a01b0383166000908152600c602052604081208054839290613333908490613b7e565b92505081905550806008600082825461334c9190613b7e565b9250508190555081600760008282546133659190613b7e565b9091555050505050565b600080670de0b6b3a76400006133858587613b3d565b61338f9190613b5c565b9050806000036133a3576064915050610e0f565b6000816133b285612710613b3d565b6133bc9190613b5c565b90507f000000000000000000000000000000000000000000000000000000000000000060006133ed826101f4613b25565b9050818311613403576064945050505050610e0f565b808310613418576103e8945050505050610e0f565b6134228282613b7e565b61342c8385613b7e565b61343890610384613b3d565b6134429190613b5c565b61344d906064613b25565b945050505050610e0f565b600060405163a9059cbb60e01b81526001600160a01b0384166004820152826024820152602060006044836000895af13d15601f3d11600160005114161716915050806125bd5760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b60448201526064016109f8565b6001600160a01b0382166000908152600d602052604090205460ff161561358457600060065460001461351c576006546005546135179184916138d5565b61351e565b815b905081600560008282546135329190613b25565b92505081905550806006600082825461354b9190613b25565b90915550506001600160a01b0383166000908152600b602052604081208054839290613578908490613b25565b90915550613123915050565b60006008546000146135a6576008546007546135a19184916138d5565b6135a8565b815b905081600760008282546135bc9190613b25565b9250508190555080600860008282546135d59190613b25565b90915550506001600160a01b0383166000908152600c602052604081208054839290613365908490613b25565b600080821361363f5760405162461bcd60e51b815260206004820152600960248201526815539111519253915160ba1b60448201526064016109f8565b5060606fffffffffffffffffffffffffffffffff821160071b82811c67ffffffffffffffff1060061b1782811c63ffffffff1060051b1782811c61ffff1060041b1782811c60ff10600390811b90911783811c600f1060021b1783811c909110600190811b90911783811c90911017609f81810383019390931b90921c6c465772b2bbbb5f824b15207a3081018102821d6d0388eaa27412d5aca026815d636e018102821d6d0df99ac502031bf953eff472fdcc018102821d6d13cdffb29d51d99322bdff5f2211018102821d6d0a0f742023def783a307a986912e018102821d6d01920d8043ca89b5239253284e42018102821d6c0b7a86d7375468fac667a0a527016c29508e458543d8aa4df2abee7882018202831d6d0139601a2efabe717e604cbb4894018202831d6d02247f7a7b6594320649aa03aba1018202831d6c8c3f38e95a6b1ff2ab1c3b343619018202831d6d02384773bdf1ac5676facced60901901820290921d6cb9a025d814b29c212b8b1a07cd190102780a09507084cc699bb0e71ea869ffffffffffffffffffffffff190105711340daa0d5f769dba1915cef59f0815a550602605f19919091017d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b302017d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b88642840160ae1d90565b60006040516323b872dd60e01b81526001600160a01b03851660048201526001600160a01b03841660248201528260448201526020600060648360008a5af13d15601f3d11600160005114161716915050806138ce5760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b60448201526064016109f8565b5050505050565b60008260001904841183021582026138ec57600080fd5b50910281810615159190040190565b6000610e0f83670de0b6b3a7640000846138d5565b600082600019048411830215820261392757600080fd5b5091020490565b80356001600160a01b03811681146126c157600080fd5b60008060006060848603121561395a57600080fd5b6139638461392e565b95602085013595506040909401359392505050565b60006020828403121561398a57600080fd5b5035919050565b600080604083850312156139a457600080fd5b6139ad8361392e565b91506139bb6020840161392e565b90509250929050565b6000602082840312156139d657600080fd5b610e0f8261392e565b8015158114612c5c57600080fd5b60008060408385031215613a0057600080fd5b613a098361392e565b91506020830135613a19816139df565b809150509250929050565b803561ffff811681146126c157600080fd5b60008060408385031215613a4957600080fd5b613a5283613a24565b91506139bb60208401613a24565b600060208284031215613a7257600080fd5b813567ffffffffffffffff81168114610e0f57600080fd5b600060208284031215613a9c57600080fd5b610e0f82613a24565b60008060408385031215613ab857600080fd5b50508035926020909101359150565b60008060008060808587031215613add57600080fd5b613ae68561392e565b935060208501359250604085013591506060850135613b04816139df565b939692955090935050565b634e487b7160e01b600052601160045260246000fd5b60008219821115613b3857613b38613b0f565b500190565b6000816000190483118215151615613b5757613b57613b0f565b500290565b600082613b7957634e487b7160e01b600052601260045260246000fd5b500490565b600082821015613b9057613b90613b0f565b500390565b600060208284031215613ba757600080fd5b8151610e0f816139df565b600061ffff83811690831681811015613bcd57613bcd613b0f565b039392505050565b6020808252600c908201526b155b985d5d1a1bdc9a5e995960a21b604082015260600190565b6020808252600f908201526e111958591b1a5b99481c185cdcd959608a1b604082015260600190565b6000600160ff1b8201613c3957613c39613b0f565b5060000390565b600060208284031215613c5257600080fd5b5051919050565b60208082526022908201527f496e73756666696369656e742072656465656d61626c6520636f6c6c61746572604082015261185b60f21b606082015260800190565b805169ffffffffffffffffffff811681146126c157600080fd5b600080600080600060a08688031215613ccd57600080fd5b613cd686613c9b565b9450602086015193506040860151925060608601519150613cf960808701613c9b565b90509295509295909350565b600060208284031215613d1757600080fd5b815160ff81168114610e0f57600080fd5b600060ff821660ff84168060ff03821115613d4557613d45613b0f565b019392505050565b600060ff821660ff841680821015613d6757613d67613b0f565b90039392505050565b600181815b80851115613dab578160001904821115613d9157613d91613b0f565b80851615613d9e57918102915b93841c9390800290613d75565b509250929050565b600082613dc2575060016124a0565b81613dcf575060006124a0565b8160018114613de55760028114613def57613e0b565b60019150506124a0565b60ff841115613e0057613e00613b0f565b50506001821b6124a0565b5060208310610133831016604e8410600b8410161715613e2e575081810a6124a0565b613e388383613d70565b8060001904821115613e4c57613e4c613b0f565b029392505050565b6000610e0f60ff841683613db3565b600060018201613e7557613e75613b0f565b5060010190565b60008060408385031215613e8f57600080fd5b505080516020909101519092909150565b60006001600160781b03808316818516808303821115613ec257613ec2613b0f565b0194935050505056fea2646970667358221220c9b20454d68d870aa705badfaee552201154cb405e93e0a8dfd7808e94448f9764736f6c634300080d003300000000000000000000000008d23468a467d2bb86fae0e32f247a26c7e2e994000000000000000000000000ea1dc98bf2308f31f3d26d40b31d4bec2d2cd61f0000000000000000000000004412fd4a4035819d201f493ada96ba7806931410000000000000000000000000cac3fd7a7d826959db74acf95d6d70545fd3ea4f0000000000000000000000006e707f6d3ae5199f142f8e9d0f40adbc7a812d7d0000000000000000000000006832802996e177660ede6095f184ec34d91c39e2000000000000000000000000ec092c15e8d5a48a77cde36827f8e228ce39471a0000000000000000000000000000000000000000000000000000000000001770000000000000000000000000000000000000000000000002b5e3af16b18800000000000000000000000000000000000000000000000000000000000001e13380

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106103a45760003560e01c806376db6c8d116101e9578063c64814dd1161010f578063df011c41116100ad578063fb529d331161007c578063fb529d3314610947578063fbfa77cf14610950578063fcad444814610977578063fe55da611461097f57600080fd5b8063df011c41146108b4578063df4fe5fa146108db578063f17336d7146108fb578063f7683bbc1461092257600080fd5b8063d854fd26116100e9578063d854fd261461084c578063d8dfeb4514610865578063d989c69c1461088c578063dba6bf1a1461089457600080fd5b8063c64814dd1461080d578063ca36b6961461083b578063cb120c3a1461084357600080fd5b8063a3a0915f11610187578063ac7e534e11610156578063ac7e534e14610798578063b56038dc146107ab578063b5e007a8146107dd578063c45a0155146107e657600080fd5b8063a3a0915f14610736578063a553762014610756578063a6afed9514610769578063ac165d7a1461077157600080fd5b8063900cf0cf116101c3578063900cf0cf146106f6578063905fa73d146106ff578063950c801014610712578063982ef0a71461072357600080fd5b806376db6c8d146106ad5780637cbc2373146106d05780638687803d146106e357600080fd5b80632f0a454e116102ce5780634b233bfa1161026c5780636682acdc1161023b5780636682acdc1461068857806371e35e84146106925780637215b0bf1461069a57806374036385146106a457600080fd5b80634b233bfa1461063957806355ac039514610642578063570ca735146106625780635a99253b1461067557600080fd5b806339cdab5c116102a857806339cdab5c146105c15780633b19f2d2146105de578063415d0f35146105f2578063483389e41461060557600080fd5b80632f0a454e146105675780632fc18d271461057a57806337a7b7d81461059a57600080fd5b8063143d4e4911610346578063220195411161031557806322019541146104fa57806322ff2a8e1461050d57806324a9d853146105205780632a87d11c1461053557600080fd5b8063143d4e49146104a4578063158b1a8b146104b957806315cf8d17146104d9578063179cac9f146104e757600080fd5b806309f6442c1161038257806309f6442c146103eb5780630b1ae707146104145780630c81eb231461044257806311df99951461046557600080fd5b806305c7bfc1146103a95780630710285c146103c5578063077e5fe3146103d8575b600080fd5b6103b260125481565b6040519081526020015b60405180910390f35b6103b26103d3366004613945565b61098d565b6103b26103e6366004613978565b610e16565b60005461040190640100000000900461ffff1681565b60405161ffff90911681526020016103bc565b60005461042c90600160701b900464ffffffffff1681565b60405164ffffffffff90911681526020016103bc565b610455610450366004613991565b610ea4565b60405190151581526020016103bc565b61048c7f0000000000000000000000004412fd4a4035819d201f493ada96ba780693141081565b6040516001600160a01b0390911681526020016103bc565b6104b76104b23660046139c4565b6110c6565b005b6103b26104c73660046139c4565b600a6020526000908152604090205481565b6001546104019061ffff1681565b6104b76104f53660046139ed565b61113a565b6104b7610508366004613a36565b611383565b6104b761051b366004613a60565b6114f1565b60005461040190600160f01b900461ffff1681565b60015461054f90600160881b90046001600160781b031681565b6040516001600160781b0390911681526020016103bc565b6104b7610575366004613945565b611636565b6103b26105883660046139c4565b600f6020526000908152604090205481565b61048c7f000000000000000000000000ea1dc98bf2308f31f3d26d40b31d4bec2d2cd61f81565b6105c9611cd4565b604080519283526020830191909152016103bc565b6000546104019062010000900461ffff1681565b6104b7610600366004613978565b611f16565b60005461062090600160301b900467ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020016103bc565b6103b260045481565b6103b2610650366004613978565b60116020526000908152604090205481565b60025461048c906001600160a01b031681565b6104b7610683366004613a8a565b611fd3565b6103b26201518081565b6104b76120c7565b6103b262015f9081565b6103b260085481565b6104556106bb3660046139c4565b600d6020526000908152604090205460ff1681565b6103b26106de366004613aa5565b612118565b6104b76106f13660046139c4565b6124a6565b6103b260095481565b6104b761070d366004613ac7565b6125a8565b6103b269021e19e0c9bab240000081565b6104b76107313660046139ed565b6125c3565b6103b26107443660046139c4565b60106020526000908152604090205481565b6103b26107643660046139c4565b61262f565b6104b76126c6565b61048c7f0000000000000000000000006e707f6d3ae5199f142f8e9d0f40adbc7a812d7d81565b60035461048c906001600160a01b031681565b6000546107c590600160981b90046001600160581b031681565b6040516001600160581b0390911681526020016103bc565b6103b260065481565b61048c7f0000000000000000000000006832802996e177660ede6095f184ec34d91c39e281565b61045561081b366004613991565b600e60209081526000928352604080842090915290825290205460ff1681565b6103b2612c5f565b6103b260055481565b60015461054f906201000090046001600160781b031681565b61048c7f00000000000000000000000008d23468a467d2bb86fae0e32f247a26c7e2e99481565b6104b7612c9f565b6103b26108a23660046139c4565b600b6020526000908152604090205481565b6103b27f000000000000000000000000000000000000000000000000000000000000177081565b6103b26108e93660046139c4565b600c6020526000908152604090205481565b6103b27f000000000000000000000000000000000000000000000002b5e3af16b188000081565b61092a612d7e565b6040805193845291151560208401521515908201526060016103bc565b6103b260075481565b61048c7f000000000000000000000000cac3fd7a7d826959db74acf95d6d70545fd3ea4f81565b6104b7612e9e565b6000546104019061ffff1681565b60006109976126c6565b6109a084612f14565b60008311610a015760405162461bcd60e51b815260206004820152602360248201527f526570617920616d6f756e74206d75737420626520677265617465722074686160448201526206e20360ec1b60648201526084015b60405180910390fd5b600080610a0c612d7e565b925050915080610a565760405162461bcd60e51b81526020600482015260156024820152741b1a5c5d5a59185d1a5bdb9cc8191a5cd8589b1959605a1b60448201526064016109f8565b6000610a618761262f565b6001600160a01b0388166000908152600a6020526040812054919250610a88828685613127565b905060008111610ada5760405162461bcd60e51b815260206004820152601e60248201527f696e73756666696369656e74206c6971756964617461626c652064656274000060448201526064016109f8565b80881115610ae6578097505b610af089896131e0565b6000610afd83878661336f565b90506000612710610b0e8382613b25565b610b18908c613b3d565b610b229190613b5c565b9050600087610b3983670de0b6b3a7640000613b3d565b610b439190613b5c565b9050848111610b525780610b54565b845b905089811015610ba65760405162461bcd60e51b815260206004820152601b60248201527f696e73756666696369656e7420636f6c6c61746572616c206f7574000000000060448201526064016109f8565b8015610c2e57610be06001600160a01b037f00000000000000000000000008d23468a467d2bb86fae0e32f247a26c7e2e994163383613458565b610bea8186613b7e565b6001600160a01b038d166000908152600a6020908152604080832093909355600d9052205460ff16610c2e578060126000828254610c289190613b7e565b90915550505b6040516323b872dd60e01b8152336004820152306024820152604481018c90526001600160a01b037f0000000000000000000000004412fd4a4035819d201f493ada96ba780693141016906323b872dd906064016020604051808303816000875af1158015610ca1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cc59190613b95565b50604051630852cd8d60e31b8152600481018c90527f0000000000000000000000004412fd4a4035819d201f493ada96ba78069314106001600160a01b0316906342966c6890602401600060405180830381600087803b158015610d2857600080fd5b505af1158015610d3c573d6000803e3d6000fd5b50505050336001600160a01b03168c6001600160a01b03167f1f0c6615429d1cdae0dfa233abf91d3b31cdbdd82c8081389832a61e1072f1ea8d84604051610d8e929190918252602082015260400190565b60405180910390a3604051630c81eb2360e01b81526001600160a01b038d1660048201523360248201523090630c81eb23906044016020604051808303816000875af1925050508015610dfe575060408051601f3d908101601f19168201909252610dfb91810190613b95565b60015b15610e0557505b9750505050505050505b9392505050565b6000600554821115610e2a57506000919050565b600080610e35612d7e565b925050915080610e49575060009392505050565b600054612710908390610e6890640100000000900461ffff1683613bb2565b61ffff16610e7e87670de0b6b3a7640000613b3d565b610e889190613b3d565b610e929190613b5c565b610e9c9190613b5c565b949350505050565b6000610eae6126c6565b610eb783612f14565b6000610ec28461262f565b905080156110bf576001600160a01b0384166000908152600a60205260408120549080610eed612d7e565b925050915080610f375760405162461bcd60e51b81526020600482015260156024820152741b1a5c5d5a59185d1a5bdb9cc8191a5cd8589b1959605a1b60448201526064016109f8565b6000670de0b6b3a7640000610f4c8585613b3d565b610f569190613b5c565b9050610f63816064613b3d565b8511156110ba57610f76886000196131e0565b6000600754600554610f889190613b25565b90508015610fee5760008160055488610fa19190613b3d565b610fab9190613b5c565b90506000610fb98289613b7e565b90508160056000828254610fcd9190613b25565b925050819055508060076000828254610fe69190613b25565b909155505050505b6110226001600160a01b037f00000000000000000000000008d23468a467d2bb86fae0e32f247a26c7e2e994168987613458565b6001600160a01b0389166000908152600d602052604090205460ff1661105a5784601260008282546110549190613b7e565b90915550505b6001600160a01b038981166000818152600a602090815260408083209290925581518a8152908101899052928b16927f10fd981a899851c05d0ff91c65c8eedbdde2dec8306e05bc4643dcba83fa2e10910160405180910390a360019650505b505050505b5092915050565b6002546001600160a01b031633146110f05760405162461bcd60e51b81526004016109f890613bd5565b600380546001600160a01b0319166001600160a01b0383169081179091556040517fc1888917e06295ab8d09c160eaaf1b4c5c251f3f2a05442fea8ad79cd43d671a90600090a250565b6111426126c6565b61114b82612f14565b336001600160a01b038316148061118557506001600160a01b0382166000908152600e6020908152604080832033845290915290205460ff165b6111a15760405162461bcd60e51b81526004016109f890613bd5565b6001600160a01b0382166000908152600d602052604090205460ff161515811515036111cb575050565b8015611232576009546001600160a01b03831660008181526010602090815260408083208590559382526011815283822054928252600f815283822092909255600a909152908120546012805491929091611227908490613b7e565b909155506112649050565b6001600160a01b0382166000908152600a6020526040812054601280549192909161125e908490613b25565b90915550505b600061126f8361262f565b9050801561131457611283836000196131e0565b6001600160a01b0383166000908152600d60205260409020805460ff19168315151790556112b183826134d9565b60006112bc8461262f565b90508181101561130e5760405162461bcd60e51b815260206004820152601b60248201527f446562742064656372656173656420756e65787065637465646c79000000000060448201526064016109f8565b50611339565b6001600160a01b0383166000908152600d60205260409020805460ff19168315151790555b826001600160a01b03167f79557fc8b078a56ef50a6155bd33758b99bfa61d29f74151f4b8aa575a3ecb1083604051611376911515815260200190565b60405180910390a2505050565b6002546001600160a01b031633146113ad5760405162461bcd60e51b81526004016109f890613bd5565b60045442106113ce5760405162461bcd60e51b81526004016109f890613bfb565b6113d66126c6565b6101f48261ffff16101580156113f457508061ffff168261ffff1611155b6114345760405162461bcd60e51b8152602060048201526011602482015270496e76616c69642073746172742062707360781b60448201526064016109f8565b8161ffff168161ffff1610158015611452575061251c8161ffff1611155b6114905760405162461bcd60e51b815260206004820152600f60248201526e496e76616c696420656e642062707360881b60448201526064016109f8565b6000805461ffff84811663ffffffff19909216821762010000918516918202179092556040805191825260208201929092527fde11d0b14fa526d6653cddfb3426cc250f98f6e72a6a36fdae6c33d85df770d1910160405180910390a15050565b6002546001600160a01b0316331461151b5760405162461bcd60e51b81526004016109f890613bd5565b600454421061153c5760405162461bcd60e51b81526004016109f890613bfb565b6115446126c6565b61a8c08167ffffffffffffffff161015801561156d575062278d008167ffffffffffffffff1611155b6115ad5760405162461bcd60e51b8152602060048201526011602482015270496e76616c69642068616c66206c69666560781b60448201526064016109f8565b8067ffffffffffffffff166115c9671bc16d674ec80000613602565b6115d39190613b5c565b600080546dffffffffffffffff0000000000001916600160301b67ffffffffffffffff9384160217905560405190821681527fcd505beca76120b741b38fe89f62433d1e15c39460e3755a9e5ca3f1f3ce7a29906020015b60405180910390a150565b61163e6126c6565b61164783612f14565b60008213156116f0576001600160a01b0383166000908152600d602052604090205460ff166116885781601260008282546116829190613b25565b90915550505b6001600160a01b0383166000908152600a6020526040812080548492906116b0908490613b25565b909155506116eb90506001600160a01b037f00000000000000000000000008d23468a467d2bb86fae0e32f247a26c7e2e99416333085613839565b611870565b6000821215611870576001600160a01b0383166000908152600d602052604090205460ff16156117db5760125461172683613c24565b6040516370a0823160e01b81523060048201527f00000000000000000000000008d23468a467d2bb86fae0e32f247a26c7e2e9946001600160a01b0316906370a0823190602401602060405180830381865afa15801561178a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117ae9190613c40565b6117b89190613b7e565b10156117d65760405162461bcd60e51b81526004016109f890613c59565b6117fb565b6117e482613c24565b601260008282546117f59190613b7e565b90915550505b61180482613c24565b6001600160a01b0384166000908152600a60205260408120805490919061182c908490613b7e565b9091555061187090503361183f84613c24565b6001600160a01b037f00000000000000000000000008d23468a467d2bb86fae0e32f247a26c7e2e994169190613458565b600081131561190a578061188484826134d9565b6040516340c10f1960e01b8152336004820152602481018290527f0000000000000000000000004412fd4a4035819d201f493ada96ba78069314106001600160a01b0316906340c10f1990604401600060405180830381600087803b1580156118ec57600080fd5b505af1158015611900573d6000803e3d6000fd5b5050505050611a67565b6000811215611a6757600061191e82613c24565b9050600061192b8561262f565b905081811161194857809150611943856000196131e0565b611952565b61195285836131e0565b6040516323b872dd60e01b8152336004820152306024820152604481018390527f0000000000000000000000004412fd4a4035819d201f493ada96ba78069314106001600160a01b0316906323b872dd906064016020604051808303816000875af11580156119c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119e99190613b95565b50604051630852cd8d60e31b8152600481018390527f0000000000000000000000004412fd4a4035819d201f493ada96ba78069314106001600160a01b0316906342966c6890602401600060405180830381600087803b158015611a4c57600080fd5b505af1158015611a60573d6000803e3d6000fd5b5050505050505b6000611a728461262f565b90508115611aff57801580611aa757507f000000000000000000000000000000000000000000000002b5e3af16b18800008110155b611aff5760405162461bcd60e51b8152602060048201526024808201527f446562742062656c6f77206d696e696d756d20616e64206c61726765722074686044820152630616e20360e41b60648201526084016109f8565b60408051848152602081018490526001600160a01b038616917f2621f237099759c662179354fad7ce6acb6344513f178385af1c93d9abb81cb1910160405180910390a260008312158015611b55575060008213155b15611b605750505050565b336001600160a01b0385161480611b9a57506001600160a01b0384166000908152600e6020908152604080832033845290915290205460ff165b611bb65760405162461bcd60e51b81526004016109f890613bd5565b80600003611bc45750505050565b600080611bcf612d7e565b50915091508015611c105760405162461bcd60e51b815260206004820152600b60248201526a526564756365206f6e6c7960a81b60448201526064016109f8565b6001600160a01b0386166000908152600a602052604081205461271090670de0b6b3a7640000907f000000000000000000000000000000000000000000000000000000000000177090611c639087613b3d565b611c6d9190613b3d565b611c779190613b5c565b611c819190613b5c565b905080841115611ccb5760405162461bcd60e51b815260206004820152601560248201527414dbdb1d995b98de4818da1958dac819985a5b1959605a1b60448201526064016109f8565b50505050505050565b6000806000807f000000000000000000000000ea1dc98bf2308f31f3d26d40b31d4bec2d2cd61f6001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015611d38573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d5c9190613cb5565b5093505092505060007f000000000000000000000000ea1dc98bf2308f31f3d26d40b31d4bec2d2cd61f6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015611dc3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611de79190613d05565b905060007f00000000000000000000000008d23468a467d2bb86fae0e32f247a26c7e2e9946001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e49573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e6d9190613d05565b90506024611e7b8284613d28565b60ff1611611ec957600082611e91836024613d4d565b611e9b9190613d4d565b905060008513611eac576000611ec1565b611eb781600a613e54565b611ec19086613b3d565b965050611f0b565b60006024611ed78385613d28565b611ee19190613d4d565b905060008513611ef2576000611f07565b611efd81600a613e54565b611f079086613b5c565b9650505b829450505050509091565b6002546001600160a01b03163314611f405760405162461bcd60e51b81526004016109f890613bd5565b611f486126c6565b6103e8811115611f885760405162461bcd60e51b815260206004820152600b60248201526a496e76616c69642066656560a81b60448201526064016109f8565b600080546001600160f01b0316600160f01b61ffff8416021790556040518181527f82f90ad483dce76534def3d808c0cb2de1d2d0cf1a52d5421d8347a8e67bd3859060200161162b565b6002546001600160a01b03163314611ffd5760405162461bcd60e51b81526004016109f890613bd5565b600454421061201e5760405162461bcd60e51b81526004016109f890613bfb565b6120266126c6565b61012c8161ffff1611156120755760405162461bcd60e51b8152602060048201526016602482015275496e76616c69642072656465656d206665652062707360501b60448201526064016109f8565b6000805465ffff00000000191664010000000061ffff8416908102919091179091556040519081527f29a575057f3ecc51f2f82c3dd11eabfd86c3a1293ce079baac1ca2cc8a4346f89060200161162b565b6002546001600160a01b031633146120f15760405162461bcd60e51b81526004016109f890613bd5565b60045442106121125760405162461bcd60e51b81526004016109f890613bfb565b42600455565b60006121226126c6565b61212b83610e16565b90508181101561217d5760405162461bcd60e51b815260206004820152601760248201527f696e73756666696369656e7420616d6f756e74206f757400000000000000000060448201526064016109f8565b6012546040516370a0823160e01b815230600482015282907f00000000000000000000000008d23468a467d2bb86fae0e32f247a26c7e2e9946001600160a01b0316906370a0823190602401602060405180830381865afa1580156121e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061220a9190613c40565b6122149190613b7e565b10156122325760405162461bcd60e51b81526004016109f890613c59565b82600560008282546122449190613b7e565b90915550506040516323b872dd60e01b8152336004820152306024820152604481018490527f0000000000000000000000004412fd4a4035819d201f493ada96ba78069314106001600160a01b0316906323b872dd906064016020604051808303816000875af11580156122bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122e09190613b95565b50604051630852cd8d60e31b8152600481018490527f0000000000000000000000004412fd4a4035819d201f493ada96ba78069314106001600160a01b0316906342966c6890602401600060405180830381600087803b15801561234357600080fd5b505af1158015612357573d6000803e3d6000fd5b505060065461237a92508391506ec097ce7bc90715b34b9f1000000000906138d5565b6009546000908152601160205260408120805490919061239b908490613b25565b909155506123d590506001600160a01b037f00000000000000000000000008d23468a467d2bb86fae0e32f247a26c7e2e994163383613458565b633b9aca006005546006546123ea9190613b5c565b1115612464576009805490600061240083613e63565b909155505060065461242a90670de0b6b3a76400006ec097ce7bc90715b34b9f10000000006138d5565b6006556009546040519081527febad8099c467528a56c98b63c8d476d251cf1ffb4c75db94b4d23fa2b6a1e3359060200160405180910390a15b604080518481526020810183905233917ff3a670cd3af7d64b488926880889d08a8585a138ff455227af6737339a1ec262910160405180910390a25b92915050565b336001600160a01b037f0000000000000000000000006832802996e177660ede6095f184ec34d91c39e216146124ee5760405162461bcd60e51b81526004016109f890613bd5565b6124f66126c6565b6001546040516340c10f1960e01b81526001600160a01b038381166004830152600160881b9092046001600160781b031660248201527f0000000000000000000000004412fd4a4035819d201f493ada96ba7806931410909116906340c10f1990604401600060405180830381600087803b15801561257457600080fd5b505af1158015612588573d6000803e3d6000fd5b50506001805470ffffffffffffffffffffffffffffffffff169055505050565b6125b2848261113a565b6125bd848484611636565b50505050565b336000818152600e602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f5e7ca79ea3ac1b19ddf506e091181a1de49af2216a6e684397d85c0757205336910160405180910390a35050565b6001600160a01b0381166000908152600d602052604081205460ff161561268f5760065415612687576005546006546001600160a01b0384166000908152600b60205260409020546126829290916138d5565b6124a0565b600092915050565b60085415612687576007546008546001600160a01b0384166000908152600c60205260409020546126829290916138d5565b919050565b600080546126e290600160701b900464ffffffffff1642613b7e565b9050806000036126ef5750565b6007546000547f0000000000000000000000006e707f6d3ae5199f142f8e9d0f40adbc7a812d7d6001600160a01b03169163ec95f34591600160981b81046001600160581b0316908590600160301b900467ffffffffffffffff16612752612c5f565b60005460405160e088901b6001600160e01b031916815260048101969096526001600160581b039094166024860152604485019290925267ffffffffffffffff166064840152608483015261ffff80821660a4840152620100009091041660c482015260e4016040805180830381865afa9250505080156127f0575060408051601f3d908101601f191682019092526127ed91810190613e7c565b60015b15612c5c57600080546127109061281290600160f01b900461ffff1684613b3d565b61281c9190613b5c565b600154909150600090612710906128379061ffff1685613b3d565b6128419190613b5c565b905081600160028282829054906101000a90046001600160781b03166128679190613ea0565b92506101000a8154816001600160781b0302191690836001600160781b0316021790555080600160118282829054906101000a90046001600160781b03166128af9190613ea0565b92506101000a8154816001600160781b0302191690836001600160781b031602179055506000816001600160781b0316836001600160781b0316856128f49190613b7e565b6128fe9190613b7e565b905060007f000000000000000000000000cac3fd7a7d826959db74acf95d6d70545fd3ea4f6001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa158015612960573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129849190613c40565b9050600754811015612ab0576007546000906129a08385613b3d565b6129aa9190613b5c565b6040516340c10f1960e01b81526001600160a01b037f000000000000000000000000cac3fd7a7d826959db74acf95d6d70545fd3ea4f81166004830152602482018390529192507f0000000000000000000000004412fd4a4035819d201f493ada96ba7806931410909116906340c10f1990604401600060405180830381600087803b158015612a3957600080fd5b505af1158015612a4d573d6000803e3d6000fd5b5050505060008184612a5f9190613b7e565b905080600160028282829054906101000a90046001600160781b0316612a859190613ea0565b92506101000a8154816001600160781b0302191690836001600160781b031602179055505050612b53565b6040516340c10f1960e01b81526001600160a01b037f000000000000000000000000cac3fd7a7d826959db74acf95d6d70545fd3ea4f81166004830152602482018490527f0000000000000000000000004412fd4a4035819d201f493ada96ba780693141016906340c10f1990604401600060405180830381600087803b158015612b3a57600080fd5b505af1158015612b4e573d6000803e3d6000fd5b505050505b8460076000828254612b659190613b25565b9091555050600080546fffffffffffffffffffffffffffffffff60701b1916600160701b4264ffffffffff16026affffffffffffffffffffff60981b191617600160981b6001600160581b0389160217905560405163dfd56cff60e01b81523060048201527f0000000000000000000000006832802996e177660ede6095f184ec34d91c39e26001600160a01b03169063dfd56cff90602401602060405180830381865afa158015612c1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c3f9190613c40565b6001805461ffff191661ffff929092169190911790555050505050505b50565b6000600554600014612c9957600554600754612c7b9190613b25565b600554612c8a90612710613b3d565b612c949190613b5c565b905090565b50600090565b6002546001600160a01b03163314612cc95760405162461bcd60e51b81526004016109f890613bd5565b612cd16126c6565b6001546040516340c10f1960e01b8152336004820152620100009091046001600160781b031660248201527f0000000000000000000000004412fd4a4035819d201f493ada96ba78069314106001600160a01b0316906340c10f1990604401600060405180830381600087803b158015612d4a57600080fd5b505af1158015612d5e573d6000803e3d6000fd5b50506001805470ffffffffffffffffffffffffffffff0000191690555050565b60008060008060019150306001600160a01b03166339cdab5c6040518163ffffffff1660e01b81526004016040805180830381865afa925050508015612de1575060408051601f3d908101601f19168201909252612dde91810190613e7c565b60015b612df2576001925060009150612e0c565b81955080925085600003612e095760019450600093505b50505b42600082821015612e1e576000612e28565b612e288383613b7e565b905062015f90811115612e8557600194506000612e4862015f9083613b7e565b905062015180811015612e7e5762015180612e638282613b7e565b612e6d9089613b3d565b612e779190613b5c565b9650612e83565b600096505b505b8515612e915785612e94565b60015b9550505050909192565b6003546001600160a01b03163314612ec85760405162461bcd60e51b81526004016109f890613bd5565b600354600280546001600160a01b0319166001600160a01b0390921691821790556040517f6317889d8001d27c355e09712356a69d4d49909c262df5cdc3d94df3fc6635b390600090a2565b6001600160a01b0381166000908152600b602052604090205480156130c9576001600160a01b038216600090815260106020908152604080832054600a835281842054600f9093529083205490925b600581108015612f74575060095484105b8015612f805750600085115b1561304157600084815260116020526040812054612f9f908490613b7e565b90506000612fbd82886ec097ce7bc90715b34b9f10000000006138d5565b9050808510612fd557612fd08186613b7e565b612fd8565b60005b9450612fe5600187613b25565b9550613000876ec097ce7bc90715b34b9f10000000006138fb565b60011461302557613020876ec097ce7bc90715b34b9f10000000006138fb565b613028565b60005b96506000935050508061303a90613e63565b9050612f63565b5083156130a057600083815260116020526040812054613062908390613b7e565b9050600061308082876ec097ce7bc90715b34b9f10000000006138d5565b9050808410613098576130938185613b7e565b61309b565b60005b935050505b506001600160a01b0384166000908152600b60209081526040808320869055600a909152902055505b6001600160a01b0382166000908152600d602052604090205460ff1615613123576009546001600160a01b03831660008181526010602090815260408083208590559382526011815283822054928252600f905291909120555b5050565b600080612710670de0b6b3a76400007f00000000000000000000000000000000000000000000000000000000000017706131618888613b3d565b61316b9190613b3d565b6131759190613b5c565b61317f9190613b5c565b905082811115613193576000915050610e0f565b61319e600484613b5c565b915069021e19e0c9bab24000008210156131d85769021e19e0c9bab240000083106131d35769021e19e0c9bab24000006131d5565b825b91505b509392505050565b6001600160a01b0382166000908152600d602052604090205460ff16156132c2576000600019820361323657506001600160a01b0382166000908152600b602052604090205461322f8361262f565b915061324a565b600654600554613247918491613910565b90505b6001600160a01b0383166000908152600b602052604081208054839290613272908490613b7e565b9091555050600654811015613294578060065461328f9190613b7e565b613297565b60005b6006556005548210156132b757816005546132b29190613b7e565b6132ba565b60005b600555505050565b600060001982036132f757506001600160a01b0382166000908152600c60205260409020546132f08361262f565b915061330b565b600854600754613308918491613910565b90505b6001600160a01b0383166000908152600c602052604081208054839290613333908490613b7e565b92505081905550806008600082825461334c9190613b7e565b9250508190555081600760008282546133659190613b7e565b9091555050505050565b600080670de0b6b3a76400006133858587613b3d565b61338f9190613b5c565b9050806000036133a3576064915050610e0f565b6000816133b285612710613b3d565b6133bc9190613b5c565b90507f000000000000000000000000000000000000000000000000000000000000177060006133ed826101f4613b25565b9050818311613403576064945050505050610e0f565b808310613418576103e8945050505050610e0f565b6134228282613b7e565b61342c8385613b7e565b61343890610384613b3d565b6134429190613b5c565b61344d906064613b25565b945050505050610e0f565b600060405163a9059cbb60e01b81526001600160a01b0384166004820152826024820152602060006044836000895af13d15601f3d11600160005114161716915050806125bd5760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b60448201526064016109f8565b6001600160a01b0382166000908152600d602052604090205460ff161561358457600060065460001461351c576006546005546135179184916138d5565b61351e565b815b905081600560008282546135329190613b25565b92505081905550806006600082825461354b9190613b25565b90915550506001600160a01b0383166000908152600b602052604081208054839290613578908490613b25565b90915550613123915050565b60006008546000146135a6576008546007546135a19184916138d5565b6135a8565b815b905081600760008282546135bc9190613b25565b9250508190555080600860008282546135d59190613b25565b90915550506001600160a01b0383166000908152600c602052604081208054839290613365908490613b25565b600080821361363f5760405162461bcd60e51b815260206004820152600960248201526815539111519253915160ba1b60448201526064016109f8565b5060606fffffffffffffffffffffffffffffffff821160071b82811c67ffffffffffffffff1060061b1782811c63ffffffff1060051b1782811c61ffff1060041b1782811c60ff10600390811b90911783811c600f1060021b1783811c909110600190811b90911783811c90911017609f81810383019390931b90921c6c465772b2bbbb5f824b15207a3081018102821d6d0388eaa27412d5aca026815d636e018102821d6d0df99ac502031bf953eff472fdcc018102821d6d13cdffb29d51d99322bdff5f2211018102821d6d0a0f742023def783a307a986912e018102821d6d01920d8043ca89b5239253284e42018102821d6c0b7a86d7375468fac667a0a527016c29508e458543d8aa4df2abee7882018202831d6d0139601a2efabe717e604cbb4894018202831d6d02247f7a7b6594320649aa03aba1018202831d6c8c3f38e95a6b1ff2ab1c3b343619018202831d6d02384773bdf1ac5676facced60901901820290921d6cb9a025d814b29c212b8b1a07cd190102780a09507084cc699bb0e71ea869ffffffffffffffffffffffff190105711340daa0d5f769dba1915cef59f0815a550602605f19919091017d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b302017d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b88642840160ae1d90565b60006040516323b872dd60e01b81526001600160a01b03851660048201526001600160a01b03841660248201528260448201526020600060648360008a5af13d15601f3d11600160005114161716915050806138ce5760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b60448201526064016109f8565b5050505050565b60008260001904841183021582026138ec57600080fd5b50910281810615159190040190565b6000610e0f83670de0b6b3a7640000846138d5565b600082600019048411830215820261392757600080fd5b5091020490565b80356001600160a01b03811681146126c157600080fd5b60008060006060848603121561395a57600080fd5b6139638461392e565b95602085013595506040909401359392505050565b60006020828403121561398a57600080fd5b5035919050565b600080604083850312156139a457600080fd5b6139ad8361392e565b91506139bb6020840161392e565b90509250929050565b6000602082840312156139d657600080fd5b610e0f8261392e565b8015158114612c5c57600080fd5b60008060408385031215613a0057600080fd5b613a098361392e565b91506020830135613a19816139df565b809150509250929050565b803561ffff811681146126c157600080fd5b60008060408385031215613a4957600080fd5b613a5283613a24565b91506139bb60208401613a24565b600060208284031215613a7257600080fd5b813567ffffffffffffffff81168114610e0f57600080fd5b600060208284031215613a9c57600080fd5b610e0f82613a24565b60008060408385031215613ab857600080fd5b50508035926020909101359150565b60008060008060808587031215613add57600080fd5b613ae68561392e565b935060208501359250604085013591506060850135613b04816139df565b939692955090935050565b634e487b7160e01b600052601160045260246000fd5b60008219821115613b3857613b38613b0f565b500190565b6000816000190483118215151615613b5757613b57613b0f565b500290565b600082613b7957634e487b7160e01b600052601260045260246000fd5b500490565b600082821015613b9057613b90613b0f565b500390565b600060208284031215613ba757600080fd5b8151610e0f816139df565b600061ffff83811690831681811015613bcd57613bcd613b0f565b039392505050565b6020808252600c908201526b155b985d5d1a1bdc9a5e995960a21b604082015260600190565b6020808252600f908201526e111958591b1a5b99481c185cdcd959608a1b604082015260600190565b6000600160ff1b8201613c3957613c39613b0f565b5060000390565b600060208284031215613c5257600080fd5b5051919050565b60208082526022908201527f496e73756666696369656e742072656465656d61626c6520636f6c6c61746572604082015261185b60f21b606082015260800190565b805169ffffffffffffffffffff811681146126c157600080fd5b600080600080600060a08688031215613ccd57600080fd5b613cd686613c9b565b9450602086015193506040860151925060608601519150613cf960808701613c9b565b90509295509295909350565b600060208284031215613d1757600080fd5b815160ff81168114610e0f57600080fd5b600060ff821660ff84168060ff03821115613d4557613d45613b0f565b019392505050565b600060ff821660ff841680821015613d6757613d67613b0f565b90039392505050565b600181815b80851115613dab578160001904821115613d9157613d91613b0f565b80851615613d9e57918102915b93841c9390800290613d75565b509250929050565b600082613dc2575060016124a0565b81613dcf575060006124a0565b8160018114613de55760028114613def57613e0b565b60019150506124a0565b60ff841115613e0057613e00613b0f565b50506001821b6124a0565b5060208310610133831016604e8410600b8410161715613e2e575081810a6124a0565b613e388383613d70565b8060001904821115613e4c57613e4c613b0f565b029392505050565b6000610e0f60ff841683613db3565b600060018201613e7557613e75613b0f565b5060010190565b60008060408385031215613e8f57600080fd5b505080516020909101519092909150565b60006001600160781b03808316818516808303821115613ec257613ec2613b0f565b0194935050505056fea2646970667358221220c9b20454d68d870aa705badfaee552201154cb405e93e0a8dfd7808e94448f9764736f6c634300080d0033

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.