ETH Price: $3,993.10 (+0.89%)

Contract

0x574C154C83432B0A45BA3ad2429C3fA242eD7359
 
Transaction Hash
Method
Block
From
To
Withdraw214039872024-12-14 23:18:112 days ago1734218291IN
Frax Finance: veFPIS Token
0 ETH0.001874637.76799598
Withdraw213968852024-12-13 23:30:473 days ago1734132647IN
Frax Finance: veFPIS Token
0 ETH0.0041604911.7247191
Withdraw213370782024-12-05 15:05:4711 days ago1733411147IN
Frax Finance: veFPIS Token
0 ETH0.0110876131.246107
Withdraw213199482024-12-03 5:40:2313 days ago1733204423IN
Frax Finance: veFPIS Token
0 ETH0.0036738814.92673696
Withdraw212842772024-11-28 5:58:4718 days ago1732773527IN
Frax Finance: veFPIS Token
0 ETH0.002319758.8127208
Withdraw212829402024-11-28 1:23:4718 days ago1732757027IN
Frax Finance: veFPIS Token
0 ETH0.0060352310.38335111
Withdraw211508722024-11-09 15:03:5937 days ago1731164639IN
Frax Finance: veFPIS Token
0 ETH0.0037159314.11677943
Withdraw211430532024-11-08 12:54:4738 days ago1731070487IN
Frax Finance: veFPIS Token
0 ETH0.0046705717.74346315
Withdraw211410702024-11-08 6:15:5938 days ago1731046559IN
Frax Finance: veFPIS Token
0 ETH0.002782367.73634503
Withdraw211076332024-11-03 14:12:2343 days ago1730643143IN
Frax Finance: veFPIS Token
0 ETH0.0039505116.05065463
Withdraw211041152024-11-03 2:25:4743 days ago1730600747IN
Frax Finance: veFPIS Token
0 ETH0.001125554.63796678
Withdraw210825482024-10-31 2:10:4746 days ago1730340647IN
Frax Finance: veFPIS Token
0 ETH0.004159468.5441106
Withdraw210130792024-10-21 9:32:4756 days ago1729503167IN
Frax Finance: veFPIS Token
0 ETH0.001877947.62993926
Withdraw210025962024-10-19 22:26:3558 days ago1729376795IN
Frax Finance: veFPIS Token
0 ETH0.001721857.1349197
Withdraw209975622024-10-19 5:35:4758 days ago1729316147IN
Frax Finance: veFPIS Token
0 ETH0.0037328814.44458206
Withdraw209890522024-10-18 1:05:4759 days ago1729213547IN
Frax Finance: veFPIS Token
0 ETH0.0049901613.87514066
Withdraw209785382024-10-16 13:51:4761 days ago1729086707IN
Frax Finance: veFPIS Token
0 ETH0.0047875319.45141253
Withdraw209779412024-10-16 11:52:1161 days ago1729079531IN
Frax Finance: veFPIS Token
0 ETH0.0040383416.73382894
Withdraw209775342024-10-16 10:29:5961 days ago1729074599IN
Frax Finance: veFPIS Token
0 ETH0.0032020312.1644735
Withdraw209774192024-10-16 10:06:4761 days ago1729073207IN
Frax Finance: veFPIS Token
0 ETH0.0027729511.26629564
Withdraw209657232024-10-14 18:54:1163 days ago1728932051IN
Frax Finance: veFPIS Token
0 ETH0.0139715829.52773753
Withdraw208801312024-10-02 20:13:3575 days ago1727900015IN
Frax Finance: veFPIS Token
0 ETH0.0071359215.19179792
Withdraw207463632024-09-14 4:07:2393 days ago1726286843IN
Frax Finance: veFPIS Token
0 ETH0.000605591.29477923
Withdraw206668832024-09-03 1:51:47104 days ago1725328307IN
Frax Finance: veFPIS Token
0 ETH0.000351541.45668974
Withdraw206443722024-08-30 22:27:35108 days ago1725056855IN
Frax Finance: veFPIS Token
0 ETH0.000361381.00484177
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Vyper_contract

Compiler Version
vyper:0.3.7

Optimization Enabled:
N/A

Other Settings:
default evmVersion, GNU GPLv2 license

Contract Source Code (Vyper language format)

# @version 0.3.7
"""
@title Voting Escrow
@author Curve Finance
@license MIT
@notice Votes have a weight depending on time, so that users are
        committed to the future of (whatever they are voting for)
@dev Vote weight decays linearly over time. Lock time cannot be
     more than `MAXTIME` (4 years).
"""

# ====================================================================
# |     ______                   _______                             |
# |    / _____________ __  __   / ____(_____  ____ _____  ________   |
# |   / /_  / ___/ __ `| |/_/  / /_  / / __ \/ __ `/ __ \/ ___/ _ \  |
# |  / __/ / /  / /_/ _>  <   / __/ / / / / / /_/ / / / / /__/  __/  |
# | /_/   /_/   \__,_/_/|_|  /_/   /_/_/ /_/\__,_/_/ /_/\___/\___/   |
# |                                                                  |
# ====================================================================
# ============================== veFPIS ==============================
# ====================================================================
# Frax Finance: https://github.com/FraxFinance

# Original idea and credit:
# Curve Finance's veCRV
# https://curve.readthedocs.io/dao-vecrv.html
# https://resources.curve.fi/faq/vote-locking-boost
# https://github.com/curvefi/curve-dao-contracts/blob/master/contracts/VotingEscrow.vy
# veFPIS is basically a fork, with the key difference that 1 FPIS locked for 1 second would be ~ 1 veFPIS,
# As opposed to ~ 0 veFPIS (as it is with veCRV)

# Frax Primary Forker(s) / Modifier(s) 
# Travis Moore: https://github.com/FortisFortuna

# Frax Reviewer(s) / Contributor(s)
# Dennis: https://github.com/denett
# Jamie Turley: https://github.com/jyturley
# Drake Evans: https://github.com/DrakeEvans
# Rich Gee: https://github.com/zer0blockchain
# Sam Kazemian: https://github.com/samkazemian

# Voting escrow to have time-weighted votes
# Votes have a weight depending on time, so that users are committed
# to the future of (whatever they are voting for).
# The weight in this implementation is linear, and lock cannot be more than maxtime:
# w ^
# 4 +        /
#   |      /
#   |    /
#   |  /
#   |/
# 1 +--------+------> time
#       maxtime (4 years)

struct Point:
    bias: int128 # principal FPIS amount locked
    slope: int128  # - dweight / dt
    ts: uint256
    blk: uint256  # block
    fpis_amt: uint256
# We cannot really do block numbers per se b/c slope is per time, not per block
# and per block could be fairly bad b/c Ethereum changes blocktimes.
# What we can do is to extrapolate ***At functions

struct LockedBalance:
    amount: int128
    end: uint256

interface ERC20:
    def decimals() -> uint256: view
    def balanceOf(addr: address) -> uint256: view
    def name() -> String[64]: view
    def symbol() -> String[32]: view
    def transfer(to: address, amount: uint256) -> bool: nonpayable
    def transferFrom(spender: address, to: address, amount: uint256) -> bool: nonpayable


# Interface for checking whether address belongs to a whitelisted
# type of a smart wallet.
# When new types are added - the whole contract is changed
# The check() method is modifying to be able to use caching
# for individual wallet addresses
interface SmartWalletChecker:
    def check(addr: address) -> bool: nonpayable

# Flags
CREATE_LOCK_TYPE: constant(int128) = 0
INCREASE_LOCK_AMOUNT: constant(int128) = 1
INCREASE_UNLOCK_TIME: constant(int128) = 2
USER_WITHDRAW: constant(int128) = 3
TRANSFER_FROM_APP: constant(int128) = 4
TRANSFER_TO_APP: constant(int128) = 5
PROXY_ADD: constant(int128) = 6
PROXY_SLASH: constant(int128) = 7
CHECKPOINT_ONLY: constant(int128) = 8

event NominateOwnership:
    admin: address

event AcceptOwnership:
    admin: address

event Deposit:
    provider: indexed(address)
    payer_addr: indexed(address)
    value: uint256
    locktime: indexed(uint256)
    type: int128
    ts: uint256

event Withdraw:
    provider: indexed(address)
    to_addr: indexed(address)
    value: uint256
    ts: uint256

event Supply:
    prevSupply: uint256
    supply: uint256

event TransferToApp:
    staker_addr: indexed(address)
    app_addr: indexed(address)
    transfer_amt: uint256

event TransferFromApp:
    app_addr: indexed(address)
    staker_addr: indexed(address)
    transfer_amt: uint256

event ProxyAdd:
    staker_addr: indexed(address)
    proxy_addr: indexed(address)
    add_amt: uint256

event ProxySlash:
    staker_addr: indexed(address)
    proxy_addr: indexed(address)
    slash_amt: uint256

event SmartWalletCheckerComitted:
    future_smart_wallet_checker: address

event SmartWalletCheckerApplied:
    smart_wallet_checker: address

event EmergencyUnlockToggled:
    emergencyUnlockActive: bool

event AppIncreaseAmountForsToggled:
    appIncreaseAmountForsEnabled: bool

event ProxyTransferFromsToggled:
    appTransferFromsEnabled: bool

event ProxyTransferTosToggled:
    appTransferTosEnabled: bool

event ProxyAddsToggled:
    proxyAddsEnabled: bool

event ProxySlashesToggled:
    proxySlashesEnabled: bool

event LendingProxySet:
    proxy_address: address

event HistoricalProxyToggled:
    proxy_address: address
    enabled: bool

event StakerProxySet:
    proxy_address: address


WEEK: constant(uint256) = 7 * 86400  # all future times are rounded by week
MAXTIME: constant(uint256) = 4 * 365 * 86400  # 4 years
MAXTIME_I128: constant(int128) = 4 * 365 * 86400  # 4 years
MULTIPLIER: constant(uint256) = 10 ** 18

VOTE_WEIGHT_MULTIPLIER: constant(uint256) = 4 - 1 # 4x gives 300% boost at 4 years
VOTE_WEIGHT_MULTIPLIER_I128: constant(int128) = 4 - 1 # 4x gives 300% boost at 4 years

token: public(address)
supply: public(uint256) # Tracked FPIS in the contract

locked: public(HashMap[address, LockedBalance]) # user -> locked balance position info

epoch: public(uint256)
point_history: public(Point[100000000000000000000000000000])  # epoch -> unsigned point
user_point_history: public(HashMap[address, Point[1000000000]])  # user -> Point[user_epoch]
user_point_epoch: public(HashMap[address, uint256]) # user -> last week epoch their slope and bias were checkpointed

# time -> signed slope change. Stored ahead of time so we can keep track of expiring users.
# Time will always be a multiple of 1 week
slope_changes: public(HashMap[uint256, int128])  
                                                 
# Misc
appIncreaseAmountForsEnabled: public(bool) # Whether the proxy can directly deposit FPIS and increase a particular user's stake 
appTransferFromsEnabled: public(bool) # Whether FPIS can be received from apps or not
appTransferTosEnabled: public(bool) # Whether FPIS can be sent to apps or not
proxyAddsEnabled: public(bool) # Whether the proxy can add to the user's position
proxySlashesEnabled: public(bool) # Whether the proxy can slash the user's position

# Emergency Unlock
emergencyUnlockActive: public(bool)

# Proxies (allow withdrawal / deposits for lending protocols, etc.)
current_proxy: public(address) # Set by admin. Can only be one at any given time
historical_proxies: public(HashMap[address, bool]) # Set by admin. Used for paying back / liquidating after the main current_proxy changes
staker_whitelisted_proxy: public(HashMap[address, address])  # user -> proxy. Set by user
user_proxy_balance: public(HashMap[address, uint256]) # user -> amount held in proxy

# ERC20 related
name: public(String[64])
symbol: public(String[32])
version: public(String[32])
decimals: public(uint256)

# Checker for whitelisted (smart contract) wallets which are allowed to deposit
# The goal is to prevent tokenizing the escrow
future_smart_wallet_checker: public(address)
smart_wallet_checker: public(address)

admin: public(address)  # Can and will be a smart contract
future_admin: public(address)


@external
def __init__():
    """
    @notice Contract constructor. No constructor args due to Etherscan verification issues
    """
    token_addr: address = 0xc2544A32872A91F4A553b404C6950e89De901fdb
    self.admin = msg.sender
    self.token = token_addr
    self.point_history[0].blk = block.number
    self.point_history[0].ts = block.timestamp
    self.point_history[0].fpis_amt = 0
    self.appTransferFromsEnabled = False
    self.appTransferTosEnabled = False
    self.proxyAddsEnabled = False
    self.proxySlashesEnabled = False

    _decimals: uint256 = ERC20(token_addr).decimals()
    assert _decimals <= 255
    self.decimals = _decimals

    self.name = "veFPIS"
    self.symbol = "veFPIS"
    self.version = "veFPIS_1.0.0"

@external
def nominate_ownership(addr: address):
    """
    @notice Transfer ownership of this contract to `addr`
    @param addr Address of the new owner
    """
    assert msg.sender == self.admin  # dev: admin only

    self.future_admin = addr
    log NominateOwnership(addr)


@external
def accept_transfer_ownership():
    """
    @notice Accept a pending ownership transfer
    @dev Only callable by the new owner
    """
    _admin: address = self.future_admin
    assert msg.sender == _admin  # dev: future admin only

    self.admin = _admin
    self.future_admin = empty(address)
    log AcceptOwnership(_admin)

@external
def commit_smart_wallet_checker(addr: address):
    """
    @notice Set an external contract to check for approved smart contract wallets
    @param addr Address of Smart contract checker
    """
    assert msg.sender == self.admin
    self.future_smart_wallet_checker = addr

    log SmartWalletCheckerComitted(self.future_smart_wallet_checker)


@external
def apply_smart_wallet_checker():
    """
    @notice Apply setting external contract to check approved smart contract wallets
    """
    assert msg.sender == self.admin
    self.smart_wallet_checker = self.future_smart_wallet_checker

    log SmartWalletCheckerApplied(self.smart_wallet_checker)

@external
def recoverERC20(token_addr: address, amount: uint256):
    """
    @dev Used to recover non-FPIS ERC20 tokens
    """
    assert msg.sender == self.admin  # dev: admin only
    assert token_addr != self.token  # Cannot recover FPIS. Use toggleEmergencyUnlock instead and have users pull theirs out individually
    ERC20(token_addr).transfer(self.admin, amount)

@internal
def assert_not_contract(addr: address):
    """
    @notice Check if the call is from a whitelisted smart contract, revert if not
    @param addr Address to be checked
    """
    if addr != tx.origin:
        checker: address = self.smart_wallet_checker
        if checker != empty(address):
            if SmartWalletChecker(checker).check(addr):
                return
        raise "Smart contract depositors not allowed"

@external
@view
def get_last_user_slope(addr: address) -> int128:
    """
    @notice Get the most recently recorded rate of voting power decrease for `addr`
    @param addr Address of the user wallet
    @return Value of the slope
    """
    uepoch: uint256 = self.user_point_epoch[addr]
    return self.user_point_history[addr][uepoch].slope

@external
@view
def get_last_user_bias(addr: address) -> int128:
    """
    @notice Get the most recently recorded bias (principal)
    @param addr Address of the user wallet
    @return Value of the bias
    """
    uepoch: uint256 = self.user_point_epoch[addr]
    return self.user_point_history[addr][uepoch].bias

@external
@view
def get_last_user_point(addr: address) -> Point:
    """
    @notice Get the most recently recorded Point for `addr`
    @param addr Address of the user wallet
    @return Latest Point for the user
    """
    uepoch: uint256 = self.user_point_epoch[addr]
    return self.user_point_history[addr][uepoch]

@external
@view
def user_point_history__ts(_addr: address, _idx: uint256) -> uint256:
    """
    @notice Get the timestamp for checkpoint `_idx` for `_addr`
    @param _addr User wallet address
    @param _idx User epoch number
    @return Epoch time of the checkpoint
    """
    return self.user_point_history[_addr][_idx].ts

@external
@view
def get_last_point() -> Point:
    """
    @notice Get the most recently recorded Point for the contract
    @return Latest Point for the contract
    """
    return self.point_history[self.epoch]

@external
@view
def locked__end(_addr: address) -> uint256:
    """
    @notice Get timestamp when `_addr`'s lock finishes
    @param _addr User wallet
    @return Epoch time of the lock end
    """
    return self.locked[_addr].end

@external
@view
def locked__amount(_addr: address) -> int128:
    """
    @notice Get amount of `_addr`'s locked FPIS
    @param _addr User wallet
    @return FPIS amount locked by `_addr`
    """
    return self.locked[_addr].amount

@external
@view
def curr_period_start() -> uint256:
    """
    @notice Get the start timestamp of this week's period
    @return Epoch time of the period start
    """
    return (block.timestamp / WEEK * WEEK)

@external
@view
def next_period_start() -> uint256:
    """
    @notice Get the start timestamp of next week's period
    @return Epoch time of next week's period start
    """
    return (WEEK + (block.timestamp / WEEK * WEEK))

@internal
def _checkpoint(addr: address, old_locked: LockedBalance, new_locked: LockedBalance, flag: int128):
    """
    @notice Record global and per-user data to checkpoint
    @param addr User's wallet address. No user checkpoint if 0x0
    @param old_locked Previous locked amount / end lock time for the user
    @param new_locked New locked amount / end lock time for the user
    @param flag Used for downstream logic
    """
    usr_old_pt: Point = empty(Point)
    usr_new_pt: Point = empty(Point)
    old_gbl_dslope: int128 = 0 # Old global slope change
    new_gbl_dslope: int128 = 0 # New global slope change
    _epoch: uint256 = self.epoch

    # ////////////////////////////////// STEP 1 //////////////////////////////////
    # Take note of any user bias and slope changes
    # Also note previous global slope and point
    # ////////////////////////////////////////////////////////////////////////////
    # Skip if a user isn't being checkpointed
    if addr != empty(address):
        # Calculate slopes and biases
        # Kept at zero when they have to

        # ==============================================================================
        # -------------------------------- Old veCRV method --------------------------------
        # if old_locked.end > block.timestamp and old_locked.amount > 0:
        #     usr_old_pt.slope = old_locked.amount / MAXTIME_I128
        #     usr_old_pt.bias = usr_old_pt.slope * convert(old_locked.end - block.timestamp, int128)
        # if new_locked.end > block.timestamp and new_locked.amount > 0:
        #     usr_new_pt.slope = new_locked.amount / MAXTIME_I128
        #     usr_new_pt.bias = usr_new_pt.slope * convert(new_locked.end - block.timestamp, int128)

        # -------------------------------- New method for veFPIS --------------------------------
        if old_locked.end > block.timestamp and old_locked.amount > 0:
            usr_old_pt.slope = (old_locked.amount * VOTE_WEIGHT_MULTIPLIER_I128) / MAXTIME_I128 
            usr_old_pt.bias = old_locked.amount + (usr_old_pt.slope * convert(old_locked.end - block.timestamp, int128))
        if new_locked.end > block.timestamp and new_locked.amount > 0:
            usr_new_pt.slope = (new_locked.amount * VOTE_WEIGHT_MULTIPLIER_I128) / MAXTIME_I128
            usr_new_pt.bias = new_locked.amount + (usr_new_pt.slope * convert(new_locked.end - block.timestamp, int128))
        # ==============================================================================

        # Read values of scheduled changes in the slope
        # old_locked.end can be in the past and in the future
        # new_locked.end can ONLY by in the FUTURE unless everything expired: than zeros
        old_gbl_dslope = self.slope_changes[old_locked.end]
        if new_locked.end != 0:
            if new_locked.end == old_locked.end:
                new_gbl_dslope = old_gbl_dslope
            else:
                new_gbl_dslope = self.slope_changes[new_locked.end]

    # Get last global point and checkpoint time
    last_point: Point = Point({bias: 0, slope: 0, ts: block.timestamp, blk: block.number, fpis_amt: 0})
    if _epoch > 0:
        last_point = self.point_history[_epoch]
    last_checkpoint: uint256 = last_point.ts

    # initial_last_point is used for extrapolation to calculate block number
    # (approximately, for *At methods) and save them
    # as we cannot figure that out exactly from inside the contract
    initial_last_point: Point = last_point

    # Calculate the average blocks per second since the last checkpoint
    # Will use this to estimate block numbers at intermediate points in Step 2
    block_slope: uint256 = 0  # dblock/dt
    if block.timestamp > last_point.ts:
        block_slope = MULTIPLIER * (block.number - last_point.blk) / (block.timestamp - last_point.ts)
    # If last point is already recorded in this block, slope=0
    # But that's ok b/c we know the block in such case


    # ////////////////////////////////// STEP 2 //////////////////////////////////
    # Go over past weeks to fill history and calculate what the current point is
    # Basically "catches up" the global state
    # Ignore user-specific deltas right now, they will be handled later
    # ////////////////////////////////////////////////////////////////////////////
    latest_checkpoint_ts: uint256 = (last_checkpoint / WEEK) * WEEK
    for i in range(255):
        # Hopefully it won't happen that this won't get used in 5 years!
        # If it does, users will be able to withdraw but vote weight will be broken
        latest_checkpoint_ts += WEEK
        d_slope: int128 = 0
        if latest_checkpoint_ts > block.timestamp:
            latest_checkpoint_ts = block.timestamp
        else:
            d_slope = self.slope_changes[latest_checkpoint_ts]

        # Subtract the elapsed bias (slope * elapsed time) from the bias total
        last_point.bias -= last_point.slope * convert(latest_checkpoint_ts - last_checkpoint, int128)

        # Add / Subtract the pre-recorded change in slope (d_slope) for this epoch to the total slope
        # d_slope can be either positive or negative
        last_point.slope += d_slope

        # Safety checks
        if last_point.bias < 0:  # This can happen
            last_point.bias = 0
        if last_point.slope < 0:  # This cannot happen - just in case
            last_point.slope = 0

        # Update the latest checkpoint, last_point info, and epoch
        last_checkpoint = latest_checkpoint_ts
        last_point.ts = latest_checkpoint_ts

        # This block number is an estimate
        last_point.blk = initial_last_point.blk + block_slope * (latest_checkpoint_ts - initial_last_point.ts) / MULTIPLIER
        _epoch += 1

        # Fill for the current block, if applicable
        if latest_checkpoint_ts == block.timestamp:
            last_point.blk = block.number
            break
        else:
            self.point_history[_epoch] = last_point

    self.epoch = _epoch
    # Now point_history is filled until t=now


    # ////////////////////////////////// STEP 3 //////////////////////////////////
    # Handle some special cases
    # ////////////////////////////////////////////////////////////////////////////
    # Skip if a user isn't being checkpointed
    if addr != empty(address):
        # If last point was in this block, the slope change has been applied already
        # But in such case we have 0 slope(s)
        last_point.slope += (usr_new_pt.slope - usr_old_pt.slope)
        last_point.bias += (usr_new_pt.bias - usr_old_pt.bias)
        

        # ==============================================================================
        # Handle FPIS balance change (withdrawals and deposits)
        if (new_locked.amount > old_locked.amount):
            last_point.fpis_amt += convert(new_locked.amount - old_locked.amount, uint256)

        if (new_locked.amount < old_locked.amount):
            last_point.fpis_amt -= convert(old_locked.amount - new_locked.amount, uint256)

            # Subtract the bias if you are slashing after expiry
            if ((flag == PROXY_SLASH) and (new_locked.end < block.timestamp)):
                # Net change is the delta
                last_point.bias += new_locked.amount
                last_point.bias -= old_locked.amount

            # Remove the offset
            # Corner case to fix issue because emergency unlock allows withdrawal before expiry and disrupts the math
            if (new_locked.amount == 0):
                if (not (self.emergencyUnlockActive)):
                    # Net change is the delta
                    # last_point.bias += new_locked.amount WILL BE ZERO
                    last_point.bias -= old_locked.amount

        # ==============================================================================

        # Check for zeroes
        if last_point.slope < 0:
            last_point.slope = 0
        if last_point.bias < 0:
            last_point.bias = 0


    # Record the changed point into history
    self.point_history[_epoch] = last_point


    # ////////////////////////////////// STEP 4 //////////////////////////////////
    # Handle global slope changes and user point historical info
    # ////////////////////////////////////////////////////////////////////////////
    # Skip if a user isn't being checkpointed
    if addr != empty(address):
        # Schedule the slope changes (slope is going down)
        # We subtract new_user_slope from [new_locked.end]
        # and add old_user_slope to [old_locked.end]
        if old_locked.end > block.timestamp:
            # old_gbl_dslope was <something> - usr_old_pt.slope, so we cancel that
            old_gbl_dslope += usr_old_pt.slope
            if new_locked.end == old_locked.end:
                old_gbl_dslope -= usr_new_pt.slope  # It was a new deposit, not extension
            self.slope_changes[old_locked.end] = old_gbl_dslope

        if new_locked.end > block.timestamp:
            if new_locked.end > old_locked.end:
                new_gbl_dslope -= usr_new_pt.slope  # old slope disappeared at this point
                self.slope_changes[new_locked.end] = new_gbl_dslope
            # else: we recorded it already in old_gbl_dslope

        # Now handle user history
        user_epoch: uint256 = self.user_point_epoch[addr] + 1

        # Update tracked info for user
        self.user_point_epoch[addr] = user_epoch
        usr_new_pt.ts = block.timestamp
        usr_new_pt.blk = block.number
        usr_new_pt.fpis_amt = convert(self.locked[addr].amount, uint256)

        # Final check
        # At the end of the day, if the user is expired, their bias should be self.locked[addr].amount (fpis_amt)
        # And their slope, 0
        if new_locked.end < block.timestamp:
            usr_new_pt.bias = self.locked[addr].amount
            usr_new_pt.slope = 0

        self.user_point_history[addr][user_epoch] = usr_new_pt


@internal
def _deposit_for(_staker_addr: address, _payer_addr: address, _value: uint256, unlock_time: uint256, locked_balance: LockedBalance, flag: int128):
    """
    @notice Deposit and lock tokens for a user
    @param _staker_addr User's wallet address
    @param _payer_addr Payer address for the deposit
    @param _value Amount to deposit
    @param unlock_time New time when to unlock the tokens, or 0 if unchanged
    @param locked_balance Previous locked amount / timestamp
    """

    # Pull in the tokens first before modifying state
    assert ERC20(self.token).transferFrom(_payer_addr, self, _value)

    # Note original data
    old_locked: LockedBalance = locked_balance
    supply_before: uint256 = self.supply

    # Get the staker's balance and the total supply
    new_locked: LockedBalance = old_locked # Will be incremented soon
    
    # Increase the supply
    self.supply = supply_before + _value

    # Add to an existing lock, or if the lock is expired - create a new one
    new_locked.amount += convert(_value, int128)
    if unlock_time != 0:
        new_locked.end = unlock_time
    self.locked[_staker_addr] = new_locked

    # Possibilities:
    # Both old_locked.end could be current or expired (>/< block.timestamp)
    # value == 0 (extend lock) or value > 0 (add to lock or extend lock)
    # new_locked.end > block.timestamp (always)
    self._checkpoint(_staker_addr, old_locked, new_locked, flag)

    log Deposit(_staker_addr, _payer_addr, _value, new_locked.end, flag, block.timestamp)
    log Supply(supply_before, supply_before + _value)


@external
def checkpoint():
    """
    @notice Record global data to checkpoint
    """
    self._checkpoint(empty(address), empty(LockedBalance), empty(LockedBalance), 0)


@external
@nonreentrant('lock')
def create_lock(_value: uint256, _unlock_time: uint256):
    """
    @notice Deposit `_value` tokens for `msg.sender` and lock until `_unlock_time`
    @param _value Amount to deposit
    @param _unlock_time Epoch time when tokens unlock, rounded down to whole weeks
    """
    self.assert_not_contract(msg.sender)
    unlock_time: uint256 = (_unlock_time / WEEK) * WEEK  # Locktime is rounded down to weeks
    _locked: LockedBalance = self.locked[msg.sender]

    assert _value > 0, "Value must be > 0"  # dev: need non-zero value
    assert _locked.amount == 0, "Withdraw old tokens first"
    assert unlock_time > block.timestamp, "Can only lock until time in the future"
    assert unlock_time <= block.timestamp + MAXTIME, "Voting lock can be 4 years max"

    self._deposit_for(msg.sender, msg.sender, _value, unlock_time, _locked, CREATE_LOCK_TYPE)

@internal
def _increase_amount(_staker_addr: address, _payer_addr: address, _value: uint256):
    """
    @notice Deposit `_value` additional tokens for `_staker_addr`
            without modifying the unlock time or creating a new stake
    @param _staker_addr The user that the tokens should be credited to
    @param _staker_addr Payer of the FPIS
    @param _value Amount of tokens to deposit and add to the lock
    """
    if ((_payer_addr != self.current_proxy) and (not self.historical_proxies[_payer_addr])):
        self.assert_not_contract(_payer_addr) # Payer should either be a proxy, EOA, or authorized contract

    self.assert_not_contract(_staker_addr) # The staker should not be unauthorized
    
    _locked: LockedBalance = self.locked[_staker_addr]
    
    assert _value > 0, "Value must be > 0"  # dev: need non-zero value
    assert _locked.amount > 0, "No existing lock found"
    assert _locked.end > block.timestamp, "Cannot add to expired lock. Withdraw"

    self._deposit_for(_staker_addr, _payer_addr, _value, 0, _locked, INCREASE_LOCK_AMOUNT)

@external
@nonreentrant('lock')
def increase_amount(_value: uint256):
    """
    @notice Deposit `_value` additional tokens for `msg.sender`
            without modifying the unlock time
    @param _value Amount of tokens to deposit and add to the lock
    """
    self._increase_amount(msg.sender, msg.sender, _value)

@external
@nonreentrant('lock')
def increase_amount_for(_staker_addr: address, _value: uint256):
    """
    @notice Deposit `_value` additional tokens for `_staker_addr`
            without modifying the unlock time or creating a new stake.
            msg.sender is payer.
    @param _staker_addr The user that the tokens should be credited to
    @param _value Amount of tokens to deposit and add to the lock
    """
    assert (self.appIncreaseAmountForsEnabled), "Currently disabled"

    # Sender is payer. Make sure to have it approve to veFPIS first
    self._increase_amount(_staker_addr, msg.sender, _value)

@external
@nonreentrant('lock')
def checkpoint_user(_staker_addr: address):
    """
    @notice Simply updates the slope, bias, etc for a user.
    @param _staker_addr The user to update
    """
    _locked: LockedBalance = self.locked[_staker_addr]
    
    assert _locked.amount > 0, "No existing lock found"
    # assert _locked.end > block.timestamp, "Expired lock"

    self._deposit_for(_staker_addr, _staker_addr, 0, 0, _locked, CHECKPOINT_ONLY)


@external
@nonreentrant('lock')
def increase_unlock_time(_unlock_time: uint256):
    """
    @notice Extend the unlock time for `msg.sender` to `_unlock_time`
    @param _unlock_time New epoch time for unlocking
    """
    self.assert_not_contract(msg.sender)
    _locked: LockedBalance = self.locked[msg.sender]
    unlock_time: uint256 = (_unlock_time / WEEK) * WEEK  # Locktime is rounded down to weeks

    assert _locked.end > block.timestamp, "Lock expired"
    assert _locked.amount > 0, "Nothing is locked"
    assert unlock_time > _locked.end, "Can only increase lock duration"
    assert unlock_time <= block.timestamp + MAXTIME, "Voting lock can be 4 years max"

    self._deposit_for(msg.sender, msg.sender, 0, unlock_time, _locked, INCREASE_UNLOCK_TIME)


@internal
def _withdraw(staker_addr: address, addr_out: address, locked_in: LockedBalance, amount_in: int128, flag: int128):
    """
    @notice Withdraw tokens for `staker_addr`
    @dev Must be greater than 0 and less than the user's locked amount
    @dev Only special users can withdraw less than the full locked amount (namely lending platforms, etc)
    """
    assert ((amount_in >= 0) and (amount_in <= locked_in.amount)), "Cannot withdraw more than the user has"
    _locked: LockedBalance = locked_in
    value: uint256 = convert(amount_in, uint256)

    old_locked: LockedBalance = _locked
    if (amount_in == _locked.amount):
        _locked.end = 0 # End the position if doing a full withdrawal
    _locked.amount -= amount_in

    self.locked[staker_addr] = _locked
    supply_before: uint256 = self.supply
    self.supply = supply_before - value

    # old_locked can have either expired <= timestamp or zero end
    # _locked has only 0 end
    # Both can have >= 0 amount
    # addr: address, old_locked: LockedBalance, new_locked: LockedBalance
    self._checkpoint(staker_addr, old_locked, _locked, flag)

    # Transfer out the tokens at the very end
    assert ERC20(self.token).transfer(addr_out, value), "ERC20 transfer out failed"

    log Withdraw(staker_addr, addr_out, value, block.timestamp)
    log Supply(supply_before, supply_before - value)

@external
@nonreentrant('proxy')
def proxy_add(
    _staker_addr: address, 
    _add_amt: uint256, 
):
    """
    @notice Proxy increaes `_staker_addr`'s veFPIS base / bias. Usually due to rewards on an app
    @param _staker_addr The target veFPIS staker address to act on
    """
    # Make sure that the function isn't disabled, and also that the proxy is valid
    assert (self.proxyAddsEnabled), "Currently disabled"
    assert (msg.sender == self.current_proxy or self.historical_proxies[msg.sender]), "Proxy not whitelisted [admin level]"
    assert (msg.sender == self.staker_whitelisted_proxy[_staker_addr]), "Proxy not whitelisted [staker level]"

    # NOTE: IF YOU ACTUALLY WANT TO TRANSFER TOKENS, YOU CAN USE
    # _deposit_for() instead of below

    # Get the staker's locked position and proxy balance
    old_locked: LockedBalance = self.locked[_staker_addr]
    _proxy_balance: uint256 = self.user_proxy_balance[_staker_addr]

    # Validate some things
    assert old_locked.amount > 0, "No existing lock found"
    assert (_add_amt) > 0, "Amount must be non-zero"

    # Increase the proxy balance 
    self.user_proxy_balance[_staker_addr] += _add_amt

    # Note original data
    supply_before: uint256 = self.supply

    # Get the staker's balance and the total supply
    new_locked: LockedBalance = old_locked # Will be incremented soon
    
    # Increase the supply
    self.supply += _add_amt

    # Add to an existing lock
    new_locked.amount += convert(_add_amt, int128)
    self.locked[_staker_addr] = new_locked

    # Checkpoint:
    self._checkpoint(_staker_addr, old_locked, new_locked, PROXY_ADD)

    # Events
    log ProxyAdd(_staker_addr, msg.sender, _add_amt)
    log Supply(supply_before, supply_before + _add_amt)


@external
@nonreentrant('proxy')
def proxy_slash(
    _staker_addr: address, 
    _slash_amt: uint256, 
):
    """
    @notice Proxy increaes `_staker_addr`'s veFPIS base / bias. Usually due to rewards on an app
    @param _staker_addr The target veFPIS staker address to act on
    """
    # Make sure that the function isn't disabled, and also that the proxy is valid
    assert (self.proxyAddsEnabled), "Currently disabled"
    assert (msg.sender == self.current_proxy or self.historical_proxies[msg.sender]), "Proxy not whitelisted [admin level]"
    assert (msg.sender == self.staker_whitelisted_proxy[_staker_addr]), "Proxy not whitelisted [staker level]"

    # NOTE: IF YOU ACTUALLY WANT TO TRANSFER TOKENS, YOU CAN USE
    # _deposit_for() instead of below

    # Get the staker's locked position and proxy balance
    old_locked: LockedBalance = self.locked[_staker_addr]
    _proxy_balance: uint256 = self.user_proxy_balance[_staker_addr]

    # Validate some things
    assert old_locked.amount > 0, "No existing lock found"
    assert (_slash_amt) > 0, "Amount must be non-zero"

    # Decrease the proxy balance 
    assert (self.user_proxy_balance[_staker_addr] >= _slash_amt), "Trying to slash too much"
    self.user_proxy_balance[_staker_addr] -= _slash_amt

    # Note original data
    supply_before: uint256 = self.supply

    # Get the staker's balance and the total supply
    new_locked: LockedBalance = old_locked # Will be decremented soon
    
    # Decrease the supply
    self.supply -= _slash_amt

    # Remove from an existing lock
    new_locked.amount -= convert(_slash_amt, int128)
    self.locked[_staker_addr] = new_locked

    # Checkpoint:
    self._checkpoint(_staker_addr, old_locked, new_locked, PROXY_SLASH)

    # Events
    log ProxyAdd(_staker_addr, msg.sender, _slash_amt)
    log Supply(supply_before, supply_before - _slash_amt)

@external
@nonreentrant('lock')
def withdraw():
    """
    @notice Withdraw all tokens for `msg.sender`
    @dev Only possible if the lock has expired or the emergency unlock is active
    @dev Also need to make sure all debts to the proxy, if any, are paid off first
    """
    # Get the staker's locked position
    _locked: LockedBalance = self.locked[msg.sender]

    # Validate some things
    assert ((block.timestamp >= _locked.end) or (self.emergencyUnlockActive)), "The lock didn't expire"
    assert (self.user_proxy_balance[msg.sender] == 0), "Outstanding FPIS in proxy"
    
    # Allow the withdrawal
    self._withdraw(msg.sender, msg.sender, _locked, _locked.amount, USER_WITHDRAW)

@external
@nonreentrant('proxy')
def transfer_from_app(_staker_addr: address, _app_addr: address, _transfer_amt: int128):
    """
    @notice Transfer tokens from a proxy-connected app to the veFPIS contract
    @dev Only possible for whitelisted proxies, both by the admin and by the staker
    @dev Only proxy and staker are checked, not the app, so make sure to do that at the proxy level
    @dev Make sure app does the approval to veFPIS first
    """
    # Make sure that the function isn't disabled, and also that the proxy is valid
    assert (self.appTransferFromsEnabled), "Currently disabled"
    assert (msg.sender == self.current_proxy or self.historical_proxies[msg.sender]), "Proxy not whitelisted [admin level]"
    assert (msg.sender == self.staker_whitelisted_proxy[_staker_addr]), "Proxy not whitelisted [staker level]"
    
    # Get the staker's locked position
    _locked: LockedBalance = self.locked[_staker_addr]
    assert _locked.amount > 0, "No existing lock found"

    # Note the amount to be moved to the veFPIS contract 
    _value: uint256 = convert(_transfer_amt, uint256)
    assert (self.user_proxy_balance[_staker_addr] >= _value), "Trying to transfer back too much"
    self.user_proxy_balance[_staker_addr] -= _value

    # Allow the transfer to the app.
    # This will not reduce the user's veFPIS balance
    assert ERC20(self.token).transferFrom(_app_addr, self, _value)

    # Checkpoint
    self._checkpoint(_staker_addr, _locked, _locked, TRANSFER_FROM_APP)

    log TransferFromApp(_app_addr, _staker_addr, _value)

@external
@nonreentrant('proxy')
def transfer_to_app(_staker_addr: address, _app_addr: address, _transfer_amt: int128):
    """
    @notice Transfer tokens for `_staker_addr` to a proxy-connected app directly, to be loaned or otherwise used
    @dev Only possible for whitelisted proxies, both by the admin and by the staker
    @dev Only proxy and staker are checked, not the app, so make sure to do that at the proxy level
    """
    # Make sure that the function isn't disabled, and also that the proxy is valid
    assert (self.appTransferTosEnabled), "Currently disabled"
    assert (msg.sender == self.current_proxy or self.historical_proxies[msg.sender]), "Proxy not whitelisted [admin level]"
    assert (msg.sender == self.staker_whitelisted_proxy[_staker_addr]), "Proxy not whitelisted [staker level]"
    
    # Get the staker's locked position
    _locked: LockedBalance = self.locked[_staker_addr]
    _locked_amt: uint256 = convert(_locked.amount, uint256)

    # Make sure the position isn't expired (no outbound transfers after expiry)
    assert (block.timestamp < _locked.end), "No transfers after expiration"

    # Note the amount to be moved to the app 
    _value: uint256 = convert(_transfer_amt, uint256)
    self.user_proxy_balance[_staker_addr] += _value

    # Make sure total user transfers do not surpass user locked balance
    assert (self.user_proxy_balance[_staker_addr] <= _locked_amt), "Amount exceeds locked balance"

    # Allow the transfer to the app.
    # This will not reduce the user's veFPIS balance
    assert ERC20(self.token).transfer(_app_addr, _value)

    # Checkpoint
    self._checkpoint(_staker_addr, _locked, _locked, TRANSFER_TO_APP)

    log TransferToApp(_staker_addr, _app_addr, _value)



@internal
@view
def find_block_epoch(_block: uint256, max_epoch: uint256) -> uint256:
    """
    @notice Binary search to estimate timestamp for block number
    @param _block Block to find
    @param max_epoch Don't go beyond this epoch
    @return Approximate timestamp for block
    """
    # Binary search
    _min: uint256 = 0
    _max: uint256 = max_epoch
    for i in range(128):  # Will be always enough for 128-bit numbers
        if _min >= _max:
            break
        _mid: uint256 = (_min + _max + 1) / 2
        if self.point_history[_mid].blk <= _block:
            _min = _mid
        else:
            _max = _mid - 1
    return _min


@external
@view
def balanceOf(addr: address, _t: uint256 = block.timestamp) -> uint256:
    """
    @notice Get the current voting power for `msg.sender`
    @dev Adheres to the ERC20 `balanceOf` interface for Aragon compatibility
    @param addr User wallet address
    @param _t Epoch time to return voting power at
    @return User voting power
    """
    _epoch: uint256 = self.user_point_epoch[addr]
    if _epoch == 0:
        return 0
    else:
        # Just leave this alone. It is fine if it decays to 1 veFPIS = 1 bias
        # Otherwise it would be inconsistent with totalSupply and totalSupplyAt
        # _locked: LockedBalance = self.locked[addr]
        # if (block.timestamp >= _locked.end): return 0

        last_point: Point = self.user_point_history[addr][_epoch]
        last_point.bias -= last_point.slope * convert(_t - last_point.ts, int128)
        if last_point.bias < 0:
            last_point.bias = 0

        # ==============================================================================
        # -------------------------------- veCRV method --------------------------------
        # weighted_supply: uint256 = convert(last_point.bias, uint256)

        # -------------------------------- veFPIS --------------------------------
        # Mainly used to counter negative biases
        weighted_supply: uint256 = convert(last_point.bias, uint256)
        if weighted_supply < last_point.fpis_amt:
            weighted_supply = last_point.fpis_amt
        

        # ==============================================================================

        return weighted_supply


@external
@view
def balanceOfAt(addr: address, _block: uint256) -> uint256:
    """
    @notice Measure voting power of `addr` at block height `_block`
    @dev Adheres to MiniMe `balanceOfAt` interface: https://github.com/Giveth/minime
    @param addr User's wallet address
    @param _block Block to calculate the voting power at
    @return Voting power
    """
    # Copying and pasting totalSupply code because Vyper cannot pass by
    # reference yet
    assert _block <= block.number

    # Binary search
    _min: uint256 = 0
    _max: uint256 = self.user_point_epoch[addr]
    for i in range(128):  # Will be always enough for 128-bit numbers
        if _min >= _max:
            break
        _mid: uint256 = (_min + _max + 1) / 2
        if self.user_point_history[addr][_mid].blk <= _block:
            _min = _mid
        else:
            _max = _mid - 1

    upoint: Point = self.user_point_history[addr][_min]

    max_epoch: uint256 = self.epoch
    _epoch: uint256 = self.find_block_epoch(_block, max_epoch)
    point_0: Point = self.point_history[_epoch]
    d_block: uint256 = 0
    d_t: uint256 = 0
    if _epoch < max_epoch:
        point_1: Point = self.point_history[_epoch + 1]
        d_block = point_1.blk - point_0.blk
        d_t = point_1.ts - point_0.ts
    else:
        d_block = block.number - point_0.blk
        d_t = block.timestamp - point_0.ts
    block_time: uint256 = point_0.ts
    if d_block != 0:
        block_time += d_t * (_block - point_0.blk) / d_block

    upoint.bias -= upoint.slope * convert(block_time - upoint.ts, int128)

    # ==============================================================================
    # -------------------------------- veCRV method --------------------------------
    # if upoint.bias >= 0:
    #     return convert(upoint.bias, uint256)
    # else:
    #     return 0

    # ----------------------------------- veFPIS -----------------------------------
    if ((upoint.bias >= 0) or (upoint.fpis_amt >= 0)):
        return convert(upoint.bias, uint256)
    else:
        return 0
    # ==============================================================================

        
@internal
@view
def supply_at(point: Point, t: uint256) -> uint256:
    """
    @notice Calculate total voting power at some point in the past
    @param point The point (bias/slope) to start search from
    @param t Time to calculate the total voting power at
    @return Total voting power at that time
    """
    last_point: Point = point
    t_i: uint256 = (last_point.ts / WEEK) * WEEK
    for i in range(255):
        t_i += WEEK
        d_slope: int128 = 0
        if t_i > t:
            t_i = t
        else:
            d_slope = self.slope_changes[t_i]
        last_point.bias -= last_point.slope * convert(t_i - last_point.ts, int128)
        if t_i == t:
            break
        last_point.slope += d_slope
        last_point.ts = t_i

    if last_point.bias < 0:
        last_point.bias = 0

    # ==============================================================================
    # ----------------------------------- veCRV ------------------------------------
    # weighted_supply: uint256 = convert(last_point.bias, uint256)

    # ----------------------------------- veFPIS -----------------------------------
    weighted_supply: uint256 = convert(last_point.bias, uint256)
    if weighted_supply < last_point.fpis_amt:
        weighted_supply = last_point.fpis_amt

    # ==============================================================================

    return weighted_supply


@external
@view
def totalSupply(t: uint256 = block.timestamp) -> uint256:
    """
    @notice Calculate total voting power
    @dev Adheres to the ERC20 `totalSupply` interface for Aragon compatibility
    @return Total voting power
    """
    _epoch: uint256 = self.epoch
    last_point: Point = self.point_history[_epoch]
    return self.supply_at(last_point, t)


@external
@view
def totalSupplyAt(_block: uint256) -> uint256:
    """
    @notice Calculate total voting power at some point in the past
    @param _block Block to calculate the total voting power at
    @return Total voting power at `_block`
    """
    assert _block <= block.number
    _epoch: uint256 = self.epoch
    target_epoch: uint256 = self.find_block_epoch(_block, _epoch)

    point: Point = self.point_history[target_epoch]
    dt: uint256 = 0
    if target_epoch < _epoch:
        point_next: Point = self.point_history[target_epoch + 1]
        if point.blk != point_next.blk:
            dt = (_block - point.blk) * (point_next.ts - point.ts) / (point_next.blk - point.blk)
    else:
        if point.blk != block.number:
            dt = (_block - point.blk) * (block.timestamp - point.ts) / (block.number - point.blk)
    # Now dt contains info on how far are we beyond point

    return self.supply_at(point, point.ts + dt)

@external
@view
def totalFPISSupply() -> uint256:
    """
    @notice Calculate FPIS supply
    @dev Adheres to the ERC20 `totalSupply` interface for Aragon compatibility
    @return Total FPIS supply
    """
    return self.supply # Don't use ERC20(self.token).balanceOf(self)

@external
@view
def totalFPISSupplyAt(_block: uint256) -> uint256:
    """
    @notice Calculate total FPIS at some point in the past
    @param _block Block to calculate the total voting power at
    @return Total FPIS supply at `_block`
    """
    assert _block <= block.number
    _epoch: uint256 = self.epoch
    target_epoch: uint256 = self.find_block_epoch(_block, _epoch)
    point: Point = self.point_history[target_epoch]
    return point.fpis_amt

@external
def toggleEmergencyUnlock():
    """
    @dev Used to allow early withdrawals of veFPIS back into FPIS, in case of an emergency
    """
    assert msg.sender == self.admin  # dev: admin only
    self.emergencyUnlockActive = not (self.emergencyUnlockActive)
    self._checkpoint(empty(address), empty(LockedBalance), empty(LockedBalance), 0)

    log EmergencyUnlockToggled(self.emergencyUnlockActive)

@external
def toggleAppIncreaseAmountFors():
    """
    @dev Toggles the ability for the proxy to directly deposit FPIS for a user, increasing their existing stake only
    """
    assert msg.sender == self.admin  # dev: admin only
    self.appIncreaseAmountForsEnabled = not (self.appIncreaseAmountForsEnabled)
    self._checkpoint(empty(address), empty(LockedBalance), empty(LockedBalance), 0)

    log AppIncreaseAmountForsToggled(self.appIncreaseAmountForsEnabled)

@external
def toggleTransferFromApp():
    """
    @dev Toggles the ability to receive FPIS from apps
    """
    assert msg.sender == self.admin  # dev: admin only
    self.appTransferFromsEnabled = not (self.appTransferFromsEnabled)
    self._checkpoint(empty(address), empty(LockedBalance), empty(LockedBalance), 0)

    log ProxyTransferFromsToggled(self.appTransferFromsEnabled)

@external
def toggleTransferToApp():
    """
    @dev Toggles the ability to send FPIS to apps
    """
    assert msg.sender == self.admin  # dev: admin only
    self.appTransferTosEnabled = not (self.appTransferTosEnabled)
    self._checkpoint(empty(address), empty(LockedBalance), empty(LockedBalance), 0)

    log ProxyTransferTosToggled(self.appTransferTosEnabled)

@external
def toggleProxyAdds():
    """
    @dev Toggles the ability for the proxy to add FPIS credit to user 
    """
    assert msg.sender == self.admin  # dev: admin only
    self.proxyAddsEnabled = not (self.proxyAddsEnabled)
    self._checkpoint(empty(address), empty(LockedBalance), empty(LockedBalance), 0)

    log ProxyAddsToggled(self.proxyAddsEnabled)


@external
def toggleProxySlashes():
    """
    @dev Toggles the ability for the proxy to subtract FPIS from a user 
    """
    assert msg.sender == self.admin  # dev: admin only
    self.proxySlashesEnabled = not (self.proxySlashesEnabled)
    self._checkpoint(empty(address), empty(LockedBalance), empty(LockedBalance), 0)

    log ProxySlashesToggled(self.proxySlashesEnabled)

@external
def adminSetProxy(_proxy: address):
    """
    @dev Admin sets the lending proxy
    @param _proxy The lending proxy address 
    """
    assert msg.sender == self.admin, "Admin only"  # dev: admin only
    self.current_proxy = _proxy
    self.historical_proxies[_proxy] = True

    log LendingProxySet(_proxy)

@external
def adminToggleHistoricalProxy(_proxy: address):
    """
    @dev Admin can manipulate a historical proxy if needed (normally done automatically in adminSetProxy)
    @dev This is needed if the main current_proxy changes and and old proxy needs to pay back or liquidate a user
    @dev Or if there is something wrong with an older proxy
    @param _proxy The lending proxy address 
    """
    assert msg.sender == self.admin, "Admin only"  # dev: admin only
    self.historical_proxies[_proxy] = not self.historical_proxies[_proxy]

    log HistoricalProxyToggled(_proxy, self.historical_proxies[_proxy])


@external
def stakerSetProxy(_proxy: address):
    """
    @dev Staker lets a particular address do activities on their behalf
    @dev Each staker can only have one proxy, to keep things / collateral / LTC calculations simple
    @param _proxy The address the staker will let withdraw/deposit for them 
    """
    # Do some checks
    assert (_proxy == empty(address) or self.current_proxy == _proxy), "Proxy not whitelisted [admin level]"
    assert (self.user_proxy_balance[msg.sender] == 0), "Outstanding FPIS in proxy"

    # Set the proxy
    self.staker_whitelisted_proxy[msg.sender] = _proxy

    log StakerProxySet(_proxy)

Contract Security Audit

Contract ABI

[{"name":"NominateOwnership","inputs":[{"name":"admin","type":"address","indexed":false}],"anonymous":false,"type":"event"},{"name":"AcceptOwnership","inputs":[{"name":"admin","type":"address","indexed":false}],"anonymous":false,"type":"event"},{"name":"Deposit","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"payer_addr","type":"address","indexed":true},{"name":"value","type":"uint256","indexed":false},{"name":"locktime","type":"uint256","indexed":true},{"name":"type","type":"int128","indexed":false},{"name":"ts","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"Withdraw","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"to_addr","type":"address","indexed":true},{"name":"value","type":"uint256","indexed":false},{"name":"ts","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"Supply","inputs":[{"name":"prevSupply","type":"uint256","indexed":false},{"name":"supply","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"TransferToApp","inputs":[{"name":"staker_addr","type":"address","indexed":true},{"name":"app_addr","type":"address","indexed":true},{"name":"transfer_amt","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"TransferFromApp","inputs":[{"name":"app_addr","type":"address","indexed":true},{"name":"staker_addr","type":"address","indexed":true},{"name":"transfer_amt","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"ProxyAdd","inputs":[{"name":"staker_addr","type":"address","indexed":true},{"name":"proxy_addr","type":"address","indexed":true},{"name":"add_amt","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"ProxySlash","inputs":[{"name":"staker_addr","type":"address","indexed":true},{"name":"proxy_addr","type":"address","indexed":true},{"name":"slash_amt","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"SmartWalletCheckerComitted","inputs":[{"name":"future_smart_wallet_checker","type":"address","indexed":false}],"anonymous":false,"type":"event"},{"name":"SmartWalletCheckerApplied","inputs":[{"name":"smart_wallet_checker","type":"address","indexed":false}],"anonymous":false,"type":"event"},{"name":"EmergencyUnlockToggled","inputs":[{"name":"emergencyUnlockActive","type":"bool","indexed":false}],"anonymous":false,"type":"event"},{"name":"AppIncreaseAmountForsToggled","inputs":[{"name":"appIncreaseAmountForsEnabled","type":"bool","indexed":false}],"anonymous":false,"type":"event"},{"name":"ProxyTransferFromsToggled","inputs":[{"name":"appTransferFromsEnabled","type":"bool","indexed":false}],"anonymous":false,"type":"event"},{"name":"ProxyTransferTosToggled","inputs":[{"name":"appTransferTosEnabled","type":"bool","indexed":false}],"anonymous":false,"type":"event"},{"name":"ProxyAddsToggled","inputs":[{"name":"proxyAddsEnabled","type":"bool","indexed":false}],"anonymous":false,"type":"event"},{"name":"ProxySlashesToggled","inputs":[{"name":"proxySlashesEnabled","type":"bool","indexed":false}],"anonymous":false,"type":"event"},{"name":"LendingProxySet","inputs":[{"name":"proxy_address","type":"address","indexed":false}],"anonymous":false,"type":"event"},{"name":"HistoricalProxyToggled","inputs":[{"name":"proxy_address","type":"address","indexed":false},{"name":"enabled","type":"bool","indexed":false}],"anonymous":false,"type":"event"},{"name":"StakerProxySet","inputs":[{"name":"proxy_address","type":"address","indexed":false}],"anonymous":false,"type":"event"},{"stateMutability":"nonpayable","type":"constructor","inputs":[],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"nominate_ownership","inputs":[{"name":"addr","type":"address"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"accept_transfer_ownership","inputs":[],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"commit_smart_wallet_checker","inputs":[{"name":"addr","type":"address"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"apply_smart_wallet_checker","inputs":[],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"recoverERC20","inputs":[{"name":"token_addr","type":"address"},{"name":"amount","type":"uint256"}],"outputs":[]},{"stateMutability":"view","type":"function","name":"get_last_user_slope","inputs":[{"name":"addr","type":"address"}],"outputs":[{"name":"","type":"int128"}]},{"stateMutability":"view","type":"function","name":"get_last_user_bias","inputs":[{"name":"addr","type":"address"}],"outputs":[{"name":"","type":"int128"}]},{"stateMutability":"view","type":"function","name":"get_last_user_point","inputs":[{"name":"addr","type":"address"}],"outputs":[{"name":"","type":"tuple","components":[{"name":"bias","type":"int128"},{"name":"slope","type":"int128"},{"name":"ts","type":"uint256"},{"name":"blk","type":"uint256"},{"name":"fpis_amt","type":"uint256"}]}]},{"stateMutability":"view","type":"function","name":"user_point_history__ts","inputs":[{"name":"_addr","type":"address"},{"name":"_idx","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"get_last_point","inputs":[],"outputs":[{"name":"","type":"tuple","components":[{"name":"bias","type":"int128"},{"name":"slope","type":"int128"},{"name":"ts","type":"uint256"},{"name":"blk","type":"uint256"},{"name":"fpis_amt","type":"uint256"}]}]},{"stateMutability":"view","type":"function","name":"locked__end","inputs":[{"name":"_addr","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"locked__amount","inputs":[{"name":"_addr","type":"address"}],"outputs":[{"name":"","type":"int128"}]},{"stateMutability":"view","type":"function","name":"curr_period_start","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"next_period_start","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"checkpoint","inputs":[],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"create_lock","inputs":[{"name":"_value","type":"uint256"},{"name":"_unlock_time","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"increase_amount","inputs":[{"name":"_value","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"increase_amount_for","inputs":[{"name":"_staker_addr","type":"address"},{"name":"_value","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"checkpoint_user","inputs":[{"name":"_staker_addr","type":"address"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"increase_unlock_time","inputs":[{"name":"_unlock_time","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"proxy_add","inputs":[{"name":"_staker_addr","type":"address"},{"name":"_add_amt","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"proxy_slash","inputs":[{"name":"_staker_addr","type":"address"},{"name":"_slash_amt","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"withdraw","inputs":[],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"transfer_from_app","inputs":[{"name":"_staker_addr","type":"address"},{"name":"_app_addr","type":"address"},{"name":"_transfer_amt","type":"int128"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"transfer_to_app","inputs":[{"name":"_staker_addr","type":"address"},{"name":"_app_addr","type":"address"},{"name":"_transfer_amt","type":"int128"}],"outputs":[]},{"stateMutability":"view","type":"function","name":"balanceOf","inputs":[{"name":"addr","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"balanceOf","inputs":[{"name":"addr","type":"address"},{"name":"_t","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"balanceOfAt","inputs":[{"name":"addr","type":"address"},{"name":"_block","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"totalSupply","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"totalSupply","inputs":[{"name":"t","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"totalSupplyAt","inputs":[{"name":"_block","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"totalFPISSupply","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"totalFPISSupplyAt","inputs":[{"name":"_block","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"toggleEmergencyUnlock","inputs":[],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"toggleAppIncreaseAmountFors","inputs":[],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"toggleTransferFromApp","inputs":[],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"toggleTransferToApp","inputs":[],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"toggleProxyAdds","inputs":[],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"toggleProxySlashes","inputs":[],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"adminSetProxy","inputs":[{"name":"_proxy","type":"address"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"adminToggleHistoricalProxy","inputs":[{"name":"_proxy","type":"address"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"stakerSetProxy","inputs":[{"name":"_proxy","type":"address"}],"outputs":[]},{"stateMutability":"view","type":"function","name":"token","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"supply","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"locked","inputs":[{"name":"arg0","type":"address"}],"outputs":[{"name":"","type":"tuple","components":[{"name":"amount","type":"int128"},{"name":"end","type":"uint256"}]}]},{"stateMutability":"view","type":"function","name":"epoch","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"point_history","inputs":[{"name":"arg0","type":"uint256"}],"outputs":[{"name":"","type":"tuple","components":[{"name":"bias","type":"int128"},{"name":"slope","type":"int128"},{"name":"ts","type":"uint256"},{"name":"blk","type":"uint256"},{"name":"fpis_amt","type":"uint256"}]}]},{"stateMutability":"view","type":"function","name":"user_point_history","inputs":[{"name":"arg0","type":"address"},{"name":"arg1","type":"uint256"}],"outputs":[{"name":"","type":"tuple","components":[{"name":"bias","type":"int128"},{"name":"slope","type":"int128"},{"name":"ts","type":"uint256"},{"name":"blk","type":"uint256"},{"name":"fpis_amt","type":"uint256"}]}]},{"stateMutability":"view","type":"function","name":"user_point_epoch","inputs":[{"name":"arg0","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"slope_changes","inputs":[{"name":"arg0","type":"uint256"}],"outputs":[{"name":"","type":"int128"}]},{"stateMutability":"view","type":"function","name":"appIncreaseAmountForsEnabled","inputs":[],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"view","type":"function","name":"appTransferFromsEnabled","inputs":[],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"view","type":"function","name":"appTransferTosEnabled","inputs":[],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"view","type":"function","name":"proxyAddsEnabled","inputs":[],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"view","type":"function","name":"proxySlashesEnabled","inputs":[],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"view","type":"function","name":"emergencyUnlockActive","inputs":[],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"view","type":"function","name":"current_proxy","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"historical_proxies","inputs":[{"name":"arg0","type":"address"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"view","type":"function","name":"staker_whitelisted_proxy","inputs":[{"name":"arg0","type":"address"}],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"user_proxy_balance","inputs":[{"name":"arg0","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"name","inputs":[],"outputs":[{"name":"","type":"string"}]},{"stateMutability":"view","type":"function","name":"symbol","inputs":[],"outputs":[{"name":"","type":"string"}]},{"stateMutability":"view","type":"function","name":"version","inputs":[],"outputs":[{"name":"","type":"string"}]},{"stateMutability":"view","type":"function","name":"decimals","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"future_smart_wallet_checker","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"smart_wallet_checker","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"admin","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"future_admin","inputs":[],"outputs":[{"name":"","type":"address"}]}]

34614c675773c2544a32872a91f4a553b404c6950e89de901fdb604052336c064f964e68233a80000000001d5560405160025543600955426008556000600a5560006c064f964e68233a80000000000a5560006c064f964e68233a80000000000b5560006c064f964e68233a80000000000c5560006c064f964e68233a80000000000d5560405163313ce567608052602060806004609c845afa6100a8573d600060003e3d6000fd5b60203d10614c6757608090505160605260ff60605111614c67576060516c064f964e68233a80000000001a5560066080527f766546504953000000000000000000000000000000000000000000000000000060a05260808051806c064f964e68233a800000000013556020820180516c064f964e68233a8000000000145550505060066080527f766546504953000000000000000000000000000000000000000000000000000060a05260808051806c064f964e68233a800000000016556020820180516c064f964e68233a80000000001755505050600c6080527f7665465049535f312e302e30000000000000000000000000000000000000000060a05260808051806c064f964e68233a800000000018556020820180516c064f964e68233a80000000001955505050614a816101e561000039614a81610000f36003361161000c576137b8565b60003560e01c34614a6f5763c35ce48d811861008d5760243610614a6f576004358060a01c614a6f576040526c064f964e68233a80000000001d543318614a6f576040516c064f964e68233a80000000001e557fd663c3b63b25bf73d75a842070d8f5c8194612f2f6483a9e5a900d9f49af63f060405160605260206060a1005b63e5ea47b8811861010c5760043610614a6f576c064f964e68233a80000000001e546040526040513318614a6f576040516c064f964e68233a80000000001d5560006c064f964e68233a80000000001e557f7f877120c72766f4eac00144c86c9e57ed52f31bac01ef5c4c223c4768a8767360405160605260206060a1005b6357f901e2811861018e5760243610614a6f576004358060a01c614a6f576040526c064f964e68233a80000000001d543318614a6f576040516c064f964e68233a80000000001b557f25ae06665d3dcefda7253511ee45f88500aab9b3ea061845a747baad51121c1e6c064f964e68233a80000000001b5460605260206060a1005b638e5b490f811861020e5760043610614a6f576c064f964e68233a80000000001d543318614a6f576c064f964e68233a80000000001b546c064f964e68233a80000000001c557f5000294582547ee919f4908d785ff270cd38f6cc318e730199ae00c65548f2ee6c064f964e68233a80000000001c5460405260206040a1005b638980f11f81186102aa5760443610614a6f576004358060a01c614a6f576040526c064f964e68233a80000000001d543318614a6f5760025460405114614a6f5760405163a9059cbb6060526c064f964e68233a80000000001d5460805260243560a052602060606044607c6000855af161028e573d600060003e3d6000fd5b60203d10614a6f576060518060011c614a6f5760c05260c05050005b637c74a174811861032c5760243610614a6f576004358060a01c614a6f576040526c064f964e68233a8000000000076040516020526000526040600020546060526c064f964e68233a80000000000660405160205260005260406000206005606051633b9ac9ff8111614a6f5702810190506001810190505460805260206080f35b631e45529b81186103a85760243610614a6f576004358060a01c614a6f576040526c064f964e68233a8000000000076040516020526000526040600020546060526c064f964e68233a80000000000660405160205260005260406000206005606051633b9ac9ff8111614a6f5702810190505460805260206080f35b63a7afdcae81186104475760243610614a6f576004358060a01c614a6f576040526c064f964e68233a8000000000076040516020526000526040600020546060526c064f964e68233a80000000000660405160205260005260406000206005606051633b9ac9ff8111614a6f5702810190508054608052600181015460a052600281015460c052600381015460e0526004810154610100525060a06080f35b63da020a1881186104a95760443610614a6f576004358060a01c614a6f576040526c064f964e68233a80000000000660405160205260005260406000206005602435633b9ac9ff8111614a6f5702810190506002810190505460605260206060f35b634181ac2781186105055760043610614a6f5760056005546c01431e0fae6d7217ca9fffffff8111614a6f5702600601805460405260018101546060526002810154608052600381015460a052600481015460c0525060a06040f35b63adc6358981186105465760243610614a6f576004358060a01c614a6f57604052600460405160205260005260406000206001810190505460605260206060f35b63570a30b781186105815760243610614a6f576004358060a01c614a6f57604052600460405160205260005260406000205460605260206060f35b63534b730e81186105ba5760043610614a6f574262093a808104905062093a8081028162093a80820418614a6f57905060405260206040f35b6308a2bc3b81186106055760043610614a6f574262093a808104905062093a8081028162093a80820418614a6f5790508062093a800162093a808110614a6f57905060405260206040f35b63c2c4c5c181186106285760043610614a6f5760c0366040376106266138b6565b005b6365fc387381186108c75760443610614a6f57600054600214614a6f576002600055336040526106566137be565b60243562093a808104905062093a8081028162093a80820418614a6f57905061066052600433602052600052604060002080546106805260018101546106a052506004356107045760116106c0527f56616c7565206d757374206265203e20300000000000000000000000000000006106e0526106c0506106c051806106e001601f826000031636823750506308c379a06106805260206106a052601f19601f6106c051011660440161069cfd5b61068051156107735760196106c0527f5769746864726177206f6c6420746f6b656e73206669727374000000000000006106e0526106c0506106c051806106e001601f826000031636823750506308c379a06106805260206106a052601f19601f6106c051011660440161069cfd5b4261066051116108085760266106c0527f43616e206f6e6c79206c6f636b20756e74696c2074696d6520696e20746865206106e0527f6675747572650000000000000000000000000000000000000000000000000000610700526106c0506106c051806106e001601f826000031636823750506308c379a06106805260206106a052601f19601f6106c051011660440161069cfd5b42630784ce008101818110614a6f57905061066051111561088957601e6106c0527f566f74696e67206c6f636b2063616e2062652034207965617273206d617800006106e0526106c0506106c051806106e001601f826000031636823750506308c379a06106805260206106a052601f19601f6106c051011660440161069cfd5b3361048052336104a0526004356104c052610660516104e05261068051610500526106a051610520526000610540526108c0614166565b6003600055005b634957677c81186109095760243610614a6f57600054600214614a6f576002600055336106605233610680526004356106a052610902614325565b6003600055005b63e864837681186109d65760443610614a6f576004358060a01c614a6f5761076052600054600214614a6f5760026000556c064f964e68233a800000000009546109b3576012610780527f43757272656e746c792064697361626c656400000000000000000000000000006107a0526107805061078051806107a001601f826000031636823750506308c379a061074052602061076052601f19601f61078051011660440161075cfd5b610760516106605233610680526024356106a0526109cf614325565b6003600055005b637de680618118610ad65760243610614a6f576004358060a01c614a6f5761066052600054600214614a6f576002600055600461066051602052600052604060002080546106805260018101546106a052506001610680511215610a9a5760166106c0527f4e6f206578697374696e67206c6f636b20666f756e64000000000000000000006106e0526106c0506106c051806106e001601f826000031636823750506308c379a06106805260206106a052601f19601f6106c051011660440161069cfd5b6106605161048052610660516104a0526040366104c03761068051610500526106a05161052052600861054052610acf614166565b6003600055005b63eff7a6128118610d585760243610614a6f57600054600214614a6f57600260005533604052610b046137be565b60043360205260005260406000208054610660526001810154610680525060043562093a808104905062093a8081028162093a80820418614a6f5790506106a052426106805111610bb557600c6106c0527f4c6f636b206578706972656400000000000000000000000000000000000000006106e0526106c0506106c051806106e001601f826000031636823750506308c379a06106805260206106a052601f19601f6106c051011660440161069cfd5b6001610660511215610c275760116106c0527f4e6f7468696e67206973206c6f636b65640000000000000000000000000000006106e0526106c0506106c051806106e001601f826000031636823750506308c379a06106805260206106a052601f19601f6106c051011660440161069cfd5b610680516106a05111610c9a57601f6106c0527f43616e206f6e6c7920696e637265617365206c6f636b206475726174696f6e006106e0526106c0506106c051806106e001601f826000031636823750506308c379a06106805260206106a052601f19601f6106c051011660440161069cfd5b42630784ce008101818110614a6f5790506106a0511115610d1b57601e6106c0527f566f74696e67206c6f636b2063616e2062652034207965617273206d617800006106e0526106c0506106c051806106e001601f826000031636823750506308c379a06106805260206106a052601f19601f6106c051011660440161069cfd5b3361048052336104a05260006104c0526106a0516104e05261066051610500526106805161052052600261054052610d51614166565b6003600055005b6361c44a7381186111f35760443610614a6f576004358060a01c614a6f5761048052600154600214614a6f5760026001556c064f964e68233a80000000000c54610e025760126104a0527f43757272656e746c792064697361626c656400000000000000000000000000006104c0526104a0506104a051806104c001601f826000031636823750506308c379a061046052602061048052601f19601f6104a051011660440161047cfd5b6c064f964e68233a80000000000f543318610e1e576001610e3a565b6c064f964e68233a800000000010336020526000526040600020545b610ec95760236104a0527f50726f7879206e6f742077686974656c6973746564205b61646d696e206c65766104c0527f656c5d00000000000000000000000000000000000000000000000000000000006104e0526104a0506104a051806104c001601f826000031636823750506308c379a061046052602061048052601f19601f6104a051011660440161047cfd5b6c064f964e68233a80000000001161048051602052600052604060002054331815610f795760246104a0527f50726f7879206e6f742077686974656c6973746564205b7374616b6572206c656104c0527f76656c5d000000000000000000000000000000000000000000000000000000006104e0526104a0506104a051806104c001601f826000031636823750506308c379a061046052602061048052601f19601f6104a051011660440161047cfd5b600461048051602052600052604060002080546104a05260018101546104c052506c064f964e68233a800000000012610480516020526000526040600020546104e05260016104a051121561102e576016610500527f4e6f206578697374696e67206c6f636b20666f756e64000000000000000000006105205261050050610500518061052001601f826000031636823750506308c379a06104c05260206104e052601f19601f6105005101166044016104dcfd5b60243561109b576017610500527f416d6f756e74206d757374206265206e6f6e2d7a65726f0000000000000000006105205261050050610500518061052001601f826000031636823750506308c379a06104c05260206104e052601f19601f6105005101166044016104dcfd5b6c064f964e68233a8000000000126104805160205260005260406000208054602435808201828110614a6f5790509050815550600354610500526104a051610520526104c05161054052600354602435808201828110614a6f57905090506003556105205160243580607f1c614a6f5780820180600f0b8118614a6f579050905061052052600461048051602052600052604060002061052051815561054051600182015550610480516040526104a0516060526104c0516080526105205160a0526105405160c052600660e0526111716138b6565b33610480517f5ac729958bdffbbceb1d50f714f86dafb13969e2023c00390189fbec3c83fc26602435610560526020610560a37f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c610500516105605261050051602435808201828110614a6f5790509050610580526040610560a16003600155005b630ce0853f811861171b5760443610614a6f576004358060a01c614a6f5761048052600154600214614a6f5760026001556c064f964e68233a80000000000c5461129d5760126104a0527f43757272656e746c792064697361626c656400000000000000000000000000006104c0526104a0506104a051806104c001601f826000031636823750506308c379a061046052602061048052601f19601f6104a051011660440161047cfd5b6c064f964e68233a80000000000f5433186112b95760016112d5565b6c064f964e68233a800000000010336020526000526040600020545b6113645760236104a0527f50726f7879206e6f742077686974656c6973746564205b61646d696e206c65766104c0527f656c5d00000000000000000000000000000000000000000000000000000000006104e0526104a0506104a051806104c001601f826000031636823750506308c379a061046052602061048052601f19601f6104a051011660440161047cfd5b6c064f964e68233a800000000011610480516020526000526040600020543318156114145760246104a0527f50726f7879206e6f742077686974656c6973746564205b7374616b6572206c656104c0527f76656c5d000000000000000000000000000000000000000000000000000000006104e0526104a0506104a051806104c001601f826000031636823750506308c379a061046052602061048052601f19601f6104a051011660440161047cfd5b600461048051602052600052604060002080546104a05260018101546104c052506c064f964e68233a800000000012610480516020526000526040600020546104e05260016104a05112156114c9576016610500527f4e6f206578697374696e67206c6f636b20666f756e64000000000000000000006105205261050050610500518061052001601f826000031636823750506308c379a06104c05260206104e052601f19601f6105005101166044016104dcfd5b602435611536576017610500527f416d6f756e74206d757374206265206e6f6e2d7a65726f0000000000000000006105205261050050610500518061052001601f826000031636823750506308c379a06104c05260206104e052601f19601f6105005101166044016104dcfd5b6024356c064f964e68233a8000000000126104805160205260005260406000205410156115c3576018610500527f547279696e6720746f20736c61736820746f6f206d75636800000000000000006105205261050050610500518061052001601f826000031636823750506308c379a06104c05260206104e052601f19601f6105005101166044016104dcfd5b6c064f964e68233a8000000000126104805160205260005260406000208054602435808203828111614a6f5790509050815550600354610500526104a051610520526104c05161054052600354602435808203828111614a6f57905090506003556105205160243580607f1c614a6f5780820380600f0b8118614a6f579050905061052052600461048051602052600052604060002061052051815561054051600182015550610480516040526104a0516060526104c0516080526105205160a0526105405160c052600760e0526116996138b6565b33610480517f5ac729958bdffbbceb1d50f714f86dafb13969e2023c00390189fbec3c83fc26602435610560526020610560a37f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c610500516105605261050051602435808203828111614a6f5790509050610580526040610560a16003600155005b633ccfd60b81186118a45760043610614a6f57600054600214614a6f576002600055600433602052600052604060002080546106c05260018101546106e052506106e05142101561177a576c064f964e68233a80000000000e5461177d565b60015b6117e7576016610700527f546865206c6f636b206469646e277420657870697265000000000000000000006107205261070050610700518061072001601f826000031636823750506308c379a06106c05260206106e052601f19601f6107005101166044016106dcfd5b6c064f964e68233a800000000012336020526000526040600020541561186d576019610700527f4f75747374616e64696e67204650495320696e2070726f7879000000000000006107205261070050610700518061072001601f826000031636823750506308c379a06106c05260206106e052601f19601f6107005101166044016106dcfd5b3361048052336104a0526106c0516104c0526106e0516104e0526106c0516105005260036105205261189d61455b565b6003600055005b63e081eec98118611d1a5760643610614a6f576004358060a01c614a6f57610480526024358060a01c614a6f576104a05260443580600f0b8118614a6f576104c052600154600214614a6f5760026001556c064f964e68233a80000000000a5461196e5760126104e0527f43757272656e746c792064697361626c65640000000000000000000000000000610500526104e0506104e0518061050001601f826000031636823750506308c379a06104a05260206104c052601f19601f6104e05101166044016104bcfd5b6c064f964e68233a80000000000f54331861198a5760016119a6565b6c064f964e68233a800000000010336020526000526040600020545b611a355760236104e0527f50726f7879206e6f742077686974656c6973746564205b61646d696e206c6576610500527f656c5d0000000000000000000000000000000000000000000000000000000000610520526104e0506104e0518061050001601f826000031636823750506308c379a06104a05260206104c052601f19601f6104e05101166044016104bcfd5b6c064f964e68233a80000000001161048051602052600052604060002054331815611ae55760246104e0527f50726f7879206e6f742077686974656c6973746564205b7374616b6572206c65610500527f76656c5d00000000000000000000000000000000000000000000000000000000610520526104e0506104e0518061050001601f826000031636823750506308c379a06104a05260206104c052601f19601f6104e05101166044016104bcfd5b600461048051602052600052604060002080546104e0526001810154610500525060016104e0511215611b78576016610520527f4e6f206578697374696e67206c6f636b20666f756e64000000000000000000006105405261052050610520518061054001601f826000031636823750506308c379a06104e052602061050052601f19601f6105205101166044016104fcfd5b6104c05160008112614a6f5761052052610520516c064f964e68233a800000000012610480516020526000526040600020541015611c16576020610540527f547279696e6720746f207472616e73666572206261636b20746f6f206d7563686105605261054050610540518061056001601f826000031636823750506308c379a061050052602061052052601f19601f61054051011660440161051cfd5b6c064f964e68233a800000000012610480516020526000526040600020805461052051808203828111614a6f57905090508155506002546323b872dd610540526104a051610560523061058052610520516105a0526020610540606461055c6000855af1611c89573d600060003e3d6000fd5b60203d10614a6f57610540518060011c614a6f576105c0526105c090505115614a6f57610480516040526104e051606052610500516080526104e05160a0526105005160c052600460e052611cdc6138b6565b610480516104a0517fbd02ce585321c0e9e7b7c479d640b5e41c559c615f32449f741727c6162f4ac861052051610540526020610540a36003600155005b632bcbc3ef81186121995760643610614a6f576004358060a01c614a6f57610480526024358060a01c614a6f576104a05260443580600f0b8118614a6f576104c052600154600214614a6f5760026001556c064f964e68233a80000000000b54611de45760126104e0527f43757272656e746c792064697361626c65640000000000000000000000000000610500526104e0506104e0518061050001601f826000031636823750506308c379a06104a05260206104c052601f19601f6104e05101166044016104bcfd5b6c064f964e68233a80000000000f543318611e00576001611e1c565b6c064f964e68233a800000000010336020526000526040600020545b611eab5760236104e0527f50726f7879206e6f742077686974656c6973746564205b61646d696e206c6576610500527f656c5d0000000000000000000000000000000000000000000000000000000000610520526104e0506104e0518061050001601f826000031636823750506308c379a06104a05260206104c052601f19601f6104e05101166044016104bcfd5b6c064f964e68233a80000000001161048051602052600052604060002054331815611f5b5760246104e0527f50726f7879206e6f742077686974656c6973746564205b7374616b6572206c65610500527f76656c5d00000000000000000000000000000000000000000000000000000000610520526104e0506104e0518061050001601f826000031636823750506308c379a06104a05260206104c052601f19601f6104e05101166044016104bcfd5b600461048051602052600052604060002080546104e052600181015461050052506104e05160008112614a6f5761052052610500514210611ffc57601d610540527f4e6f207472616e73666572732061667465722065787069726174696f6e0000006105605261054050610540518061056001601f826000031636823750506308c379a061050052602061052052601f19601f61054051011660440161051cfd5b6104c05160008112614a6f57610540526c064f964e68233a800000000012610480516020526000526040600020805461054051808201828110614a6f5790509050815550610520516c064f964e68233a8000000000126104805160205260005260406000205411156120ce57601d610560527f416d6f756e742065786365656473206c6f636b65642062616c616e63650000006105805261056050610560518061058001601f826000031636823750506308c379a061052052602061054052601f19601f61056051011660440161053cfd5b60025463a9059cbb610560526104a05161058052610540516105a0526020610560604461057c6000855af1612108573d600060003e3d6000fd5b60203d10614a6f57610560518060011c614a6f576105c0526105c090505115614a6f57610480516040526104e051606052610500516080526104e05160a0526105005160c052600560e05261215b6138b6565b6104a051610480517f9b4385b7a76ef579e481bdac2c47bfd58d1bb8635d3898165499b2326f1f0f8a61054051610560526020610560a36003600155005b6370a0823181186121b55760243610614a6f57426060526121ce565b62fdd58e81186123125760443610614a6f576024356060525b6004358060a01c614a6f576040526c064f964e68233a80000000000760405160205260005260406000205460805260805161221557600060a052602060a061231056612310565b6c064f964e68233a80000000000660405160205260005260406000206005608051633b9ac9ff8111614a6f570281019050805460a052600181015460c052600281015460e0526003810154610100526004810154610120525060a05160c05160605160e051808203828111614a6f579050905080607f1c614a6f5780820280600f0b8118614a6f579050905080820380600f0b8118614a6f579050905060a0527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60a051136122e457600060a0525b60a05160008112614a6f57610140526101205161014051101561230a5761012051610140525b60206101405bf35b634ee2cd7e81186126c05760443610614a6f576004358060a01c614a6f57610100524360243511614a6f576000610120526c064f964e68233a800000000007610100516020526000526040600020546101405260006080905b806101605261014051610120511061238257612420565b6101205161014051808201828110614a6f579050905060018101818110614a6f5790508060011c9050610180526024356c064f964e68233a800000000006610100516020526000526040600020600561018051633b9ac9ff8111614a6f57028101905060038101905054111561240c576101805160018103818111614a6f57905061014052612415565b61018051610120525b60010181811861236b575b50506c064f964e68233a800000000006610100516020526000526040600020600561012051633b9ac9ff8111614a6f57028101905080546101605260018101546101805260028101546101a05260038101546101c05260048101546101e05250600554610200526024356040526102005160605261249f610240614825565b61024051610220526005610220516c01431e0fae6d7217ca9fffffff8111614a6f570260060180546102405260018101546102605260028101546102805260038101546102a05260048101546102c052506040366102e03761020051610220511061253757436102a051808203828111614a6f57905090506102e0524261028051808203828111614a6f5790509050610300526125c2565b60056102205160018101818110614a6f5790506c01431e0fae6d7217ca9fffffff8111614a6f570260060180546103205260018101546103405260028101546103605260038101546103805260048101546103a05250610380516102a051808203828111614a6f57905090506102e0526103605161028051808203828111614a6f5790509050610300525b61028051610320526102e051156126285761032051610300516024356102a051808203828111614a6f5790509050808202811583838304141715614a6f57905090506102e0518015614a6f5780820490509050808201828110614a6f5790509050610320525b6101605161018051610320516101a051808203828111614a6f579050905080607f1c614a6f5780820280600f0b8118614a6f579050905080820380600f0b8118614a6f579050905061016052600061016051121561268d5760006101e0511015612690565b60015b6126a85760006103405260206103406126be566126be565b6101605160008112614a6f576103405260206103405bf35b6318160ddd81186126dd5760043610614a6f5742610200526126f8565b63bd85b03981186127845760243610614a6f57600435610200525b600554610220526005610220516c01431e0fae6d7217ca9fffffff8111614a6f570260060180546102405260018101546102605260028101546102805260038101546102a05260048101546102c0525060206102405160405261026051606052610280516080526102a05160a0526102c05160c0526102005160e05261277f6102e06148d5565b6102e0f35b63981b24d081186129aa5760243610614a6f574360043511614a6f5760055461020052600435604052610200516060526127bf610240614825565b61024051610220526005610220516c01431e0fae6d7217ca9fffffff8111614a6f570260060180546102405260018101546102605260028101546102805260038101546102a05260048101546102c0525060006102e05261020051610220511061289257436102a0511461295c576004356102a051808203828111614a6f57905090504261028051808203828111614a6f5790509050808202811583838304141715614a6f5790509050436102a051808203828111614a6f57905090508015614a6f57808204905090506102e05261295c565b60056102205160018101818110614a6f5790506c01431e0fae6d7217ca9fffffff8111614a6f570260060180546103005260018101546103205260028101546103405260038101546103605260048101546103805250610360516102a0511461295c576004356102a051808203828111614a6f57905090506103405161028051808203828111614a6f5790509050808202811583838304141715614a6f5790509050610360516102a051808203828111614a6f57905090508015614a6f57808204905090506102e0525b60206102405160405261026051606052610280516080526102a05160a0526102c05160c052610280516102e051808201828110614a6f579050905060e0526129a56103006148d5565b610300f35b6347c7341e81186129c95760043610614a6f5760035460405260206040f35b630f4174a08118612a5c5760243610614a6f574360043511614a6f576005546101005260043560405261010051606052612a04610140614825565b61014051610120526005610120516c01431e0fae6d7217ca9fffffff8111614a6f570260060180546101405260018101546101605260028101546101805260038101546101a05260048101546101c0525060206101c0f35b6388c2b3e38118612aed5760043610614a6f576c064f964e68233a80000000001d543318614a6f576c064f964e68233a80000000000e54156c064f964e68233a80000000000e5560c036604037612ab16138b6565b7feab47e2228a51fa30e80a103a8e0105cb452653bb433513d6bf11e3e3f3637a36c064f964e68233a80000000000e54610480526020610480a1005b6316f616728118612b7e5760043610614a6f576c064f964e68233a80000000001d543318614a6f576c064f964e68233a80000000000954156c064f964e68233a8000000000095560c036604037612b426138b6565b7fdf5e6b6b37885e5046023bc28abaf4fa317db98e6c992dbba3830fd69f0f03d46c064f964e68233a80000000000954610480526020610480a1005b630b33ac488118612c0f5760043610614a6f576c064f964e68233a80000000001d543318614a6f576c064f964e68233a80000000000a54156c064f964e68233a80000000000a5560c036604037612bd36138b6565b7f924855749e6db89374eeb6d4f88b5c3f2c3f4efff48671af2f5ebab4a558faf66c064f964e68233a80000000000a54610480526020610480a1005b63e8ff7e828118612ca05760043610614a6f576c064f964e68233a80000000001d543318614a6f576c064f964e68233a80000000000b54156c064f964e68233a80000000000b5560c036604037612c646138b6565b7f2e0793e6a4f1baa7980928be0706d6246f765c4c1038e17ef46dd004504b65fa6c064f964e68233a80000000000b54610480526020610480a1005b63172cb9998118612d315760043610614a6f576c064f964e68233a80000000001d543318614a6f576c064f964e68233a80000000000c54156c064f964e68233a80000000000c5560c036604037612cf56138b6565b7f0e9338cb371255221e6df7c319bbd0290c0a544046aaf2d21b42ff796e6f71746c064f964e68233a80000000000c54610480526020610480a1005b630929f36f8118612dc25760043610614a6f576c064f964e68233a80000000001d543318614a6f576c064f964e68233a80000000000d54156c064f964e68233a80000000000d5560c036604037612d866138b6565b7f3f6a71816fba143945ad3184a7c46790c48e516749eb3cc736342f5bb3eb88cb6c064f964e68233a80000000000d54610480526020610480a1005b63bacec9678118612eb55760243610614a6f576004358060a01c614a6f576040526c064f964e68233a80000000001d54331815612e5657600a6060527f41646d696e206f6e6c790000000000000000000000000000000000000000000060805260605060605180608001601f826000031636823750506308c379a06020526020604052601f19601f6060510116604401603cfd5b6040516c064f964e68233a80000000000f5560016c064f964e68233a8000000000106040516020526000526040600020557f3c3597bbc034da275532ab551a8916a9bade58914700b6aa1ab96f16243bafaa60405160605260206060a1005b63417f25c98118612fd25760243610614a6f576004358060a01c614a6f576040526c064f964e68233a80000000001d54331815612f4957600a6060527f41646d696e206f6e6c790000000000000000000000000000000000000000000060805260605060605180608001601f826000031636823750506308c379a06020526020604052601f19601f6060510116604401603cfd5b6c064f964e68233a800000000010604051602052600052604060002054156c064f964e68233a8000000000106040516020526000526040600020557f94fd3c4f99e5feb7efd85143b048ed68169886a015308a163bfdc9b36a8188596040516060526c064f964e68233a80000000001060405160205260005260406000205460805260406060a1005b634d857d0c81186131645760243610614a6f576004358060a01c614a6f57604052604051613001576001613016565b6040516c064f964e68233a80000000000f5418155b61309b5760236060527f50726f7879206e6f742077686974656c6973746564205b61646d696e206c65766080527f656c5d000000000000000000000000000000000000000000000000000000000060a05260605060605180608001601f826000031636823750506308c379a06020526020604052601f19601f6060510116604401603cfd5b6c064f964e68233a80000000001233602052600052604060002054156131185760196060527f4f75747374616e64696e67204650495320696e2070726f78790000000000000060805260605060605180608001601f826000031636823750506308c379a06020526020604052601f19601f6060510116604401603cfd5b6040516c064f964e68233a800000000011336020526000526040600020557f0c5eabcbebbb87a106d2bdf51ee002efdd0f1b547dae5f9d6d0e28b15af217db60405160605260206060a1005b63fc0c546a81186131835760043610614a6f5760025460405260206040f35b63047fc9aa81186131a25760043610614a6f5760035460405260206040f35b63cbf9fe5f81186131e75760243610614a6f576004358060a01c614a6f5760405260046040516020526000526040600020805460605260018101546080525060406060f35b63900cf0cf81186132065760043610614a6f5760055460405260206040f35b63d1febfb981186132625760243610614a6f5760056004356c01431e0fae6d7217ca9fffffff8111614a6f5702600601805460405260018101546060526002810154608052600381015460a052600481015460c0525060a06040f35b6328d09d4781186132e05760443610614a6f576004358060a01c614a6f576040526c064f964e68233a80000000000660405160205260005260406000206005602435633b9ac9ff8111614a6f57028101905080546060526001810154608052600281015460a052600381015460c052600481015460e0525060a06060f35b63010ae75781186133275760243610614a6f576004358060a01c614a6f576040526c064f964e68233a80000000000760405160205260005260406000205460605260206060f35b637119748481186133605760243610614a6f576c064f964e68233a80000000000860043560205260005260406000205460405260206040f35b63702254a1811861338b5760043610614a6f576c064f964e68233a8000000000095460405260206040f35b63811d35ba81186133b65760043610614a6f576c064f964e68233a80000000000a5460405260206040f35b63a61f15c481186133e15760043610614a6f576c064f964e68233a80000000000b5460405260206040f35b6395957b57811861340c5760043610614a6f576c064f964e68233a80000000000c5460405260206040f35b632e67535d81186134375760043610614a6f576c064f964e68233a80000000000d5460405260206040f35b63f894648581186134625760043610614a6f576c064f964e68233a80000000000e5460405260206040f35b630a5de864811861348d5760043610614a6f576c064f964e68233a80000000000f5460405260206040f35b63d5a7505081186134d45760243610614a6f576004358060a01c614a6f576040526c064f964e68233a80000000001060405160205260005260406000205460605260206060f35b638d94781a811861351b5760243610614a6f576004358060a01c614a6f576040526c064f964e68233a80000000001160405160205260005260406000205460605260206060f35b63312afb0381186135625760243610614a6f576004358060a01c614a6f576040526c064f964e68233a80000000001260405160205260005260406000205460605260206060f35b6306fdde0381186135ff5760043610614a6f57602080604052806040016c064f964e68233a8000000000135480825260208201600082601f0160051c60028111614a6f5780156135d157905b806c064f964e68233a80000000001401548160051b8401526001018181186135ae575b505050508051806020830101601f82600003163682375050601f19601f825160200101169050810190506040f35b6395d89b41811861366f5760043610614a6f57602080604052806040016c064f964e68233a80000000001654808252602082016c064f964e68233a80000000001754815250508051806020830101601f82600003163682375050601f19601f825160200101169050810190506040f35b6354fd4d5081186136df5760043610614a6f57602080604052806040016c064f964e68233a80000000001854808252602082016c064f964e68233a80000000001954815250508051806020830101601f82600003163682375050601f19601f825160200101169050810190506040f35b63313ce567811861370a5760043610614a6f576c064f964e68233a80000000001a5460405260206040f35b638ff36fd181186137355760043610614a6f576c064f964e68233a80000000001b5460405260206040f35b637175d4f781186137605760043610614a6f576c064f964e68233a80000000001c5460405260206040f35b63f851a440811861378b5760043610614a6f576c064f964e68233a80000000001d5460405260206040f35b6317f7182a81186137b65760043610614a6f576c064f964e68233a80000000001e5460405260206040f35b505b60006000fd5b32604051146138b4576c064f964e68233a80000000001c54606052606051156138335760605163c23697a860805260405160a052602060806024609c6000855af161380e573d600060003e3d6000fd5b60203d10614a6f576080518060011c614a6f5760c05260c090505115613833576138b4565b60256080527f536d61727420636f6e7472616374206465706f7369746f7273206e6f7420616c60a0527f6c6f77656400000000000000000000000000000000000000000000000000000060c0526080506080518060a001601f826000031636823750506308c379a06040526020606052601f19601f6080510116604401605cfd5b565b61018036610100376005546102805260405115613a3a5742608051116138dd5760006138e5565b600160605112155b15613952576060516003810280600f0b8118614a6f579050630784ce0081059050610120526060516101205160805142808203828111614a6f579050905080607f1c614a6f5780820280600f0b8118614a6f579050905080820180600f0b8118614a6f5790509050610100525b4260c0511161396257600061396a565b600160a05112155b156139d75760a0516003810280600f0b8118614a6f579050630784ce00810590506101c05260a0516101c05160c05142808203828111614a6f579050905080607f1c614a6f5780820280600f0b8118614a6f579050905080820180600f0b8118614a6f57905090506101a0525b6c064f964e68233a8000000000086080516020526000526040600020546102405260c05115613a3a5760805160c05118613a18576102405161026052613a3a565b6c064f964e68233a80000000000860c051602052600052604060002054610260525b6040366102a037426102e05243610300526000610320526102805115613aa4576005610280516c01431e0fae6d7217ca9fffffff8111614a6f570260060180546102a05260018101546102c05260028101546102e052600381015461030052600481015461032052505b6102e051610340526102a051610360526102c051610380526102e0516103a052610300516103c052610320516103e0526000610400526102e051421115613b3b574361030051808203828111614a6f5790509050670de0b6b3a7640000810281670de0b6b3a7640000820418614a6f579050426102e051808203828111614a6f57905090508015614a6f5780820490509050610400525b6103405162093a808104905062093a8081028162093a80820418614a6f57905061042052600060ff905b80610440526104205162093a808101818110614a6f57905061042052600061046052426104205111613bb8576c064f964e68233a8000000000086104205160205260005260406000205461046052613bbe565b42610420525b6102a0516102c0516104205161034051808203828111614a6f579050905080607f1c614a6f5780820280600f0b8118614a6f579050905080820380600f0b8118614a6f57905090506102a0526102c0516104605180820180600f0b8118614a6f57905090506102c0527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6102a05113613c585760006102a0525b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6102c05113613c895760006102c0525b6104205161034052610420516102e0526103c05161040051610420516103a051808203828111614a6f5790509050808202811583838304141715614a6f5790509050670de0b6b3a764000081049050808201828110614a6f5790509050610300526102805160018101818110614a6f57905061028052426104205118613d17574361030052613d6c56613d61565b6005610280516c01431e0fae6d7217ca9fffffff8111614a6f57026006016102a05181556102c05160018201556102e0516002820155610300516003820155610320516004820155505b600101818118613b65575b50506102805160055560405115613f52576102c0516101c0516101205180820380600f0b8118614a6f579050905080820180600f0b8118614a6f57905090506102c0526102a0516101a0516101005180820380600f0b8118614a6f579050905080820180600f0b8118614a6f57905090506102a05260605160a0511315613e23576103205160a05160605180820380600f0b8118614a6f579050905060008112614a6f57808201828110614a6f5790509050610320525b60605160a0511215613ef0576103205160605160a05180820380600f0b8118614a6f579050905060008112614a6f57808203828111614a6f579050905061032052600760e05118613e78574260c05110613e7b565b60005b15613eb9576102a05160a05180820180600f0b8118614a6f57905090506102a0526102a05160605180820380600f0b8118614a6f57905090506102a0525b60a051613ef0576c064f964e68233a80000000000e54613ef0576102a05160605180820380600f0b8118614a6f57905090506102a0525b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6102c05113613f215760006102c0525b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6102a05113613f525760006102a0525b6005610280516c01431e0fae6d7217ca9fffffff8111614a6f57026006016102a05181556102c05160018201556102e051600282015561030051600382015561032051600482015550604051156141645742608051111561401557610240516101205180820180600f0b8118614a6f57905090506102405260805160c05118613ff357610240516101c05180820380600f0b8118614a6f5790509050610240525b610240516c064f964e68233a8000000000086080516020526000526040600020555b4260c051111561406a5760805160c051111561406a57610260516101c05180820380600f0b8118614a6f579050905061026052610260516c064f964e68233a80000000000860c0516020526000526040600020555b6c064f964e68233a80000000000760405160205260005260406000205460018101818110614a6f57905061044052610440516c064f964e68233a800000000007604051602052600052604060002055426101e0524361020052600460405160205260005260406000205460008112614a6f57610220524260c05110156141065760046040516020526000526040600020546101a05260006101c0525b6c064f964e68233a8000000000066040516020526000526040600020600561044051633b9ac9ff8111614a6f5702810190506101a05181556101c05160018201556101e0516002820155610200516003820155610220516004820155505b565b6002546323b872dd610560526104a05161058052306105a0526104c0516105c0526020610560606461057c6000855af16141a5573d600060003e3d6000fd5b60203d10614a6f57610560518060011c614a6f576105e0526105e090505115614a6f57610500516105605261052051610580526003546105a052610560516105c052610580516105e0526105a0516104c051808201828110614a6f57905090506003556105c0516104c05180607f1c614a6f5780820180600f0b8118614a6f57905090506105c0526104e0511561423f576104e0516105e0525b60046104805160205260005260406000206105c05181556105e0516001820155506104805160405261056051606052610580516080526105c05160a0526105e05160c0526105405160e0526142926138b6565b6105e0516104a051610480517f4ace3cb811d903eba44ce1721d1a1d79232246711977f44236000551f8c11cc16104c05161060052610540516106205242610640526060610600a47f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c6105a051610600526105a0516104c051808201828110614a6f5790509050610620526040610600a1565b6c064f964e68233a80000000000f546106805114614361576c064f964e68233a8000000000106106805160205260005260406000205415614364565b60005b1561437857610680516040526143786137be565b610660516040526143876137be565b600461066051602052600052604060002080546106c05260018101546106e052506106a051614416576011610700527f56616c7565206d757374206265203e20300000000000000000000000000000006107205261070050610700518061072001601f826000031636823750506308c379a06106c05260206106e052601f19601f6107005101166044016106dcfd5b60016106c0511215614488576016610700527f4e6f206578697374696e67206c6f636b20666f756e64000000000000000000006107205261070050610700518061072001601f826000031636823750506308c379a06106c05260206106e052601f19601f6107005101166044016106dcfd5b426106e0511161451d576024610700527f43616e6e6f742061646420746f2065787069726564206c6f636b2e2057697468610720527f64726177000000000000000000000000000000000000000000000000000000006107405261070050610700518061072001601f826000031636823750506308c379a06106c05260206106e052601f19601f6107005101166044016106dcfd5b6106605161048052610680516104a0526106a0516104c05260006104e0526106c051610500526106e05161052052600161054052614559614166565b565b600061050051121561456e576000614579565b6104c0516105005113155b614608576026610540527f43616e6e6f74207769746864726177206d6f7265207468616e20746865207573610560527f65722068617300000000000000000000000000000000000000000000000000006105805261054050610540518061056001601f826000031636823750506308c379a061050052602061052052601f19601f61054051011660440161051cfd5b6104c051610540526104e051610560526105005160008112614a6f5761058052610540516105a052610560516105c05261054051610500511861464c576000610560525b610540516105005180820380600f0b8118614a6f5790509050610540526004610480516020526000526040600020610540518155610560516001820155506003546105e0526105e05161058051808203828111614a6f5790509050600355610480516040526105a0516060526105c0516080526105405160a0526105605160c0526105205160e0526146dc6138b6565b60025463a9059cbb610600526104a0516106205261058051610640526020610600604461061c6000855af1614716573d600060003e3d6000fd5b60203d10614a6f57610600518060011c614a6f576106605261066090505161479e576019610680527f4552433230207472616e73666572206f7574206661696c6564000000000000006106a0526106805061068051806106a001601f826000031636823750506308c379a061064052602061066052601f19601f61068051011660440161065cfd5b6104a051610480517ff341246adaac6f497bc2a656f546ab9e182111d630394f0c57c710a59a2cb567610580516106005242610620526040610600a37f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c6105e051610600526105e05161058051808203828111614a6f5790509050610620526040610600a1565b600060805260605160a05260006080905b8060c05260a0516080511061484a576148cb565b60805160a051808201828110614a6f579050905060018101818110614a6f5790508060011c905060e052604051600560e0516c01431e0fae6d7217ca9fffffff8111614a6f57026006016003810190505411156148b95760e05160018103818111614a6f57905060a0526148c0565b60e0516080525b600101818118614836575b5050608051815250565b60405161010052606051610120526080516101405260a0516101605260c051610180526101405162093a808104905062093a8081028162093a80820418614a6f5790506101a052600060ff905b806101c0526101a05162093a808101818110614a6f5790506101a05260006101e05260e0516101a05111614977576c064f964e68233a8000000000086101a0516020526000526040600020546101e05261497f565b60e0516101a0525b61010051610120516101a05161014051808203828111614a6f579050905080607f1c614a6f5780820280600f0b8118614a6f579050905080820380600f0b8118614a6f57905090506101005260e0516101a051186149dc57614a0c565b610120516101e05180820180600f0b8118614a6f5790509050610120526101a05161014052600101818118614922575b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101005113614a3f576000610100525b6101005160008112614a6f576101c052610180516101c0511015614a6657610180516101c0525b6101c051815250565b600080fda165767970657283000307000b005b600080fd

Deployed Bytecode

0x6003361161000c576137b8565b60003560e01c34614a6f5763c35ce48d811861008d5760243610614a6f576004358060a01c614a6f576040526c064f964e68233a80000000001d543318614a6f576040516c064f964e68233a80000000001e557fd663c3b63b25bf73d75a842070d8f5c8194612f2f6483a9e5a900d9f49af63f060405160605260206060a1005b63e5ea47b8811861010c5760043610614a6f576c064f964e68233a80000000001e546040526040513318614a6f576040516c064f964e68233a80000000001d5560006c064f964e68233a80000000001e557f7f877120c72766f4eac00144c86c9e57ed52f31bac01ef5c4c223c4768a8767360405160605260206060a1005b6357f901e2811861018e5760243610614a6f576004358060a01c614a6f576040526c064f964e68233a80000000001d543318614a6f576040516c064f964e68233a80000000001b557f25ae06665d3dcefda7253511ee45f88500aab9b3ea061845a747baad51121c1e6c064f964e68233a80000000001b5460605260206060a1005b638e5b490f811861020e5760043610614a6f576c064f964e68233a80000000001d543318614a6f576c064f964e68233a80000000001b546c064f964e68233a80000000001c557f5000294582547ee919f4908d785ff270cd38f6cc318e730199ae00c65548f2ee6c064f964e68233a80000000001c5460405260206040a1005b638980f11f81186102aa5760443610614a6f576004358060a01c614a6f576040526c064f964e68233a80000000001d543318614a6f5760025460405114614a6f5760405163a9059cbb6060526c064f964e68233a80000000001d5460805260243560a052602060606044607c6000855af161028e573d600060003e3d6000fd5b60203d10614a6f576060518060011c614a6f5760c05260c05050005b637c74a174811861032c5760243610614a6f576004358060a01c614a6f576040526c064f964e68233a8000000000076040516020526000526040600020546060526c064f964e68233a80000000000660405160205260005260406000206005606051633b9ac9ff8111614a6f5702810190506001810190505460805260206080f35b631e45529b81186103a85760243610614a6f576004358060a01c614a6f576040526c064f964e68233a8000000000076040516020526000526040600020546060526c064f964e68233a80000000000660405160205260005260406000206005606051633b9ac9ff8111614a6f5702810190505460805260206080f35b63a7afdcae81186104475760243610614a6f576004358060a01c614a6f576040526c064f964e68233a8000000000076040516020526000526040600020546060526c064f964e68233a80000000000660405160205260005260406000206005606051633b9ac9ff8111614a6f5702810190508054608052600181015460a052600281015460c052600381015460e0526004810154610100525060a06080f35b63da020a1881186104a95760443610614a6f576004358060a01c614a6f576040526c064f964e68233a80000000000660405160205260005260406000206005602435633b9ac9ff8111614a6f5702810190506002810190505460605260206060f35b634181ac2781186105055760043610614a6f5760056005546c01431e0fae6d7217ca9fffffff8111614a6f5702600601805460405260018101546060526002810154608052600381015460a052600481015460c0525060a06040f35b63adc6358981186105465760243610614a6f576004358060a01c614a6f57604052600460405160205260005260406000206001810190505460605260206060f35b63570a30b781186105815760243610614a6f576004358060a01c614a6f57604052600460405160205260005260406000205460605260206060f35b63534b730e81186105ba5760043610614a6f574262093a808104905062093a8081028162093a80820418614a6f57905060405260206040f35b6308a2bc3b81186106055760043610614a6f574262093a808104905062093a8081028162093a80820418614a6f5790508062093a800162093a808110614a6f57905060405260206040f35b63c2c4c5c181186106285760043610614a6f5760c0366040376106266138b6565b005b6365fc387381186108c75760443610614a6f57600054600214614a6f576002600055336040526106566137be565b60243562093a808104905062093a8081028162093a80820418614a6f57905061066052600433602052600052604060002080546106805260018101546106a052506004356107045760116106c0527f56616c7565206d757374206265203e20300000000000000000000000000000006106e0526106c0506106c051806106e001601f826000031636823750506308c379a06106805260206106a052601f19601f6106c051011660440161069cfd5b61068051156107735760196106c0527f5769746864726177206f6c6420746f6b656e73206669727374000000000000006106e0526106c0506106c051806106e001601f826000031636823750506308c379a06106805260206106a052601f19601f6106c051011660440161069cfd5b4261066051116108085760266106c0527f43616e206f6e6c79206c6f636b20756e74696c2074696d6520696e20746865206106e0527f6675747572650000000000000000000000000000000000000000000000000000610700526106c0506106c051806106e001601f826000031636823750506308c379a06106805260206106a052601f19601f6106c051011660440161069cfd5b42630784ce008101818110614a6f57905061066051111561088957601e6106c0527f566f74696e67206c6f636b2063616e2062652034207965617273206d617800006106e0526106c0506106c051806106e001601f826000031636823750506308c379a06106805260206106a052601f19601f6106c051011660440161069cfd5b3361048052336104a0526004356104c052610660516104e05261068051610500526106a051610520526000610540526108c0614166565b6003600055005b634957677c81186109095760243610614a6f57600054600214614a6f576002600055336106605233610680526004356106a052610902614325565b6003600055005b63e864837681186109d65760443610614a6f576004358060a01c614a6f5761076052600054600214614a6f5760026000556c064f964e68233a800000000009546109b3576012610780527f43757272656e746c792064697361626c656400000000000000000000000000006107a0526107805061078051806107a001601f826000031636823750506308c379a061074052602061076052601f19601f61078051011660440161075cfd5b610760516106605233610680526024356106a0526109cf614325565b6003600055005b637de680618118610ad65760243610614a6f576004358060a01c614a6f5761066052600054600214614a6f576002600055600461066051602052600052604060002080546106805260018101546106a052506001610680511215610a9a5760166106c0527f4e6f206578697374696e67206c6f636b20666f756e64000000000000000000006106e0526106c0506106c051806106e001601f826000031636823750506308c379a06106805260206106a052601f19601f6106c051011660440161069cfd5b6106605161048052610660516104a0526040366104c03761068051610500526106a05161052052600861054052610acf614166565b6003600055005b63eff7a6128118610d585760243610614a6f57600054600214614a6f57600260005533604052610b046137be565b60043360205260005260406000208054610660526001810154610680525060043562093a808104905062093a8081028162093a80820418614a6f5790506106a052426106805111610bb557600c6106c0527f4c6f636b206578706972656400000000000000000000000000000000000000006106e0526106c0506106c051806106e001601f826000031636823750506308c379a06106805260206106a052601f19601f6106c051011660440161069cfd5b6001610660511215610c275760116106c0527f4e6f7468696e67206973206c6f636b65640000000000000000000000000000006106e0526106c0506106c051806106e001601f826000031636823750506308c379a06106805260206106a052601f19601f6106c051011660440161069cfd5b610680516106a05111610c9a57601f6106c0527f43616e206f6e6c7920696e637265617365206c6f636b206475726174696f6e006106e0526106c0506106c051806106e001601f826000031636823750506308c379a06106805260206106a052601f19601f6106c051011660440161069cfd5b42630784ce008101818110614a6f5790506106a0511115610d1b57601e6106c0527f566f74696e67206c6f636b2063616e2062652034207965617273206d617800006106e0526106c0506106c051806106e001601f826000031636823750506308c379a06106805260206106a052601f19601f6106c051011660440161069cfd5b3361048052336104a05260006104c0526106a0516104e05261066051610500526106805161052052600261054052610d51614166565b6003600055005b6361c44a7381186111f35760443610614a6f576004358060a01c614a6f5761048052600154600214614a6f5760026001556c064f964e68233a80000000000c54610e025760126104a0527f43757272656e746c792064697361626c656400000000000000000000000000006104c0526104a0506104a051806104c001601f826000031636823750506308c379a061046052602061048052601f19601f6104a051011660440161047cfd5b6c064f964e68233a80000000000f543318610e1e576001610e3a565b6c064f964e68233a800000000010336020526000526040600020545b610ec95760236104a0527f50726f7879206e6f742077686974656c6973746564205b61646d696e206c65766104c0527f656c5d00000000000000000000000000000000000000000000000000000000006104e0526104a0506104a051806104c001601f826000031636823750506308c379a061046052602061048052601f19601f6104a051011660440161047cfd5b6c064f964e68233a80000000001161048051602052600052604060002054331815610f795760246104a0527f50726f7879206e6f742077686974656c6973746564205b7374616b6572206c656104c0527f76656c5d000000000000000000000000000000000000000000000000000000006104e0526104a0506104a051806104c001601f826000031636823750506308c379a061046052602061048052601f19601f6104a051011660440161047cfd5b600461048051602052600052604060002080546104a05260018101546104c052506c064f964e68233a800000000012610480516020526000526040600020546104e05260016104a051121561102e576016610500527f4e6f206578697374696e67206c6f636b20666f756e64000000000000000000006105205261050050610500518061052001601f826000031636823750506308c379a06104c05260206104e052601f19601f6105005101166044016104dcfd5b60243561109b576017610500527f416d6f756e74206d757374206265206e6f6e2d7a65726f0000000000000000006105205261050050610500518061052001601f826000031636823750506308c379a06104c05260206104e052601f19601f6105005101166044016104dcfd5b6c064f964e68233a8000000000126104805160205260005260406000208054602435808201828110614a6f5790509050815550600354610500526104a051610520526104c05161054052600354602435808201828110614a6f57905090506003556105205160243580607f1c614a6f5780820180600f0b8118614a6f579050905061052052600461048051602052600052604060002061052051815561054051600182015550610480516040526104a0516060526104c0516080526105205160a0526105405160c052600660e0526111716138b6565b33610480517f5ac729958bdffbbceb1d50f714f86dafb13969e2023c00390189fbec3c83fc26602435610560526020610560a37f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c610500516105605261050051602435808201828110614a6f5790509050610580526040610560a16003600155005b630ce0853f811861171b5760443610614a6f576004358060a01c614a6f5761048052600154600214614a6f5760026001556c064f964e68233a80000000000c5461129d5760126104a0527f43757272656e746c792064697361626c656400000000000000000000000000006104c0526104a0506104a051806104c001601f826000031636823750506308c379a061046052602061048052601f19601f6104a051011660440161047cfd5b6c064f964e68233a80000000000f5433186112b95760016112d5565b6c064f964e68233a800000000010336020526000526040600020545b6113645760236104a0527f50726f7879206e6f742077686974656c6973746564205b61646d696e206c65766104c0527f656c5d00000000000000000000000000000000000000000000000000000000006104e0526104a0506104a051806104c001601f826000031636823750506308c379a061046052602061048052601f19601f6104a051011660440161047cfd5b6c064f964e68233a800000000011610480516020526000526040600020543318156114145760246104a0527f50726f7879206e6f742077686974656c6973746564205b7374616b6572206c656104c0527f76656c5d000000000000000000000000000000000000000000000000000000006104e0526104a0506104a051806104c001601f826000031636823750506308c379a061046052602061048052601f19601f6104a051011660440161047cfd5b600461048051602052600052604060002080546104a05260018101546104c052506c064f964e68233a800000000012610480516020526000526040600020546104e05260016104a05112156114c9576016610500527f4e6f206578697374696e67206c6f636b20666f756e64000000000000000000006105205261050050610500518061052001601f826000031636823750506308c379a06104c05260206104e052601f19601f6105005101166044016104dcfd5b602435611536576017610500527f416d6f756e74206d757374206265206e6f6e2d7a65726f0000000000000000006105205261050050610500518061052001601f826000031636823750506308c379a06104c05260206104e052601f19601f6105005101166044016104dcfd5b6024356c064f964e68233a8000000000126104805160205260005260406000205410156115c3576018610500527f547279696e6720746f20736c61736820746f6f206d75636800000000000000006105205261050050610500518061052001601f826000031636823750506308c379a06104c05260206104e052601f19601f6105005101166044016104dcfd5b6c064f964e68233a8000000000126104805160205260005260406000208054602435808203828111614a6f5790509050815550600354610500526104a051610520526104c05161054052600354602435808203828111614a6f57905090506003556105205160243580607f1c614a6f5780820380600f0b8118614a6f579050905061052052600461048051602052600052604060002061052051815561054051600182015550610480516040526104a0516060526104c0516080526105205160a0526105405160c052600760e0526116996138b6565b33610480517f5ac729958bdffbbceb1d50f714f86dafb13969e2023c00390189fbec3c83fc26602435610560526020610560a37f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c610500516105605261050051602435808203828111614a6f5790509050610580526040610560a16003600155005b633ccfd60b81186118a45760043610614a6f57600054600214614a6f576002600055600433602052600052604060002080546106c05260018101546106e052506106e05142101561177a576c064f964e68233a80000000000e5461177d565b60015b6117e7576016610700527f546865206c6f636b206469646e277420657870697265000000000000000000006107205261070050610700518061072001601f826000031636823750506308c379a06106c05260206106e052601f19601f6107005101166044016106dcfd5b6c064f964e68233a800000000012336020526000526040600020541561186d576019610700527f4f75747374616e64696e67204650495320696e2070726f7879000000000000006107205261070050610700518061072001601f826000031636823750506308c379a06106c05260206106e052601f19601f6107005101166044016106dcfd5b3361048052336104a0526106c0516104c0526106e0516104e0526106c0516105005260036105205261189d61455b565b6003600055005b63e081eec98118611d1a5760643610614a6f576004358060a01c614a6f57610480526024358060a01c614a6f576104a05260443580600f0b8118614a6f576104c052600154600214614a6f5760026001556c064f964e68233a80000000000a5461196e5760126104e0527f43757272656e746c792064697361626c65640000000000000000000000000000610500526104e0506104e0518061050001601f826000031636823750506308c379a06104a05260206104c052601f19601f6104e05101166044016104bcfd5b6c064f964e68233a80000000000f54331861198a5760016119a6565b6c064f964e68233a800000000010336020526000526040600020545b611a355760236104e0527f50726f7879206e6f742077686974656c6973746564205b61646d696e206c6576610500527f656c5d0000000000000000000000000000000000000000000000000000000000610520526104e0506104e0518061050001601f826000031636823750506308c379a06104a05260206104c052601f19601f6104e05101166044016104bcfd5b6c064f964e68233a80000000001161048051602052600052604060002054331815611ae55760246104e0527f50726f7879206e6f742077686974656c6973746564205b7374616b6572206c65610500527f76656c5d00000000000000000000000000000000000000000000000000000000610520526104e0506104e0518061050001601f826000031636823750506308c379a06104a05260206104c052601f19601f6104e05101166044016104bcfd5b600461048051602052600052604060002080546104e0526001810154610500525060016104e0511215611b78576016610520527f4e6f206578697374696e67206c6f636b20666f756e64000000000000000000006105405261052050610520518061054001601f826000031636823750506308c379a06104e052602061050052601f19601f6105205101166044016104fcfd5b6104c05160008112614a6f5761052052610520516c064f964e68233a800000000012610480516020526000526040600020541015611c16576020610540527f547279696e6720746f207472616e73666572206261636b20746f6f206d7563686105605261054050610540518061056001601f826000031636823750506308c379a061050052602061052052601f19601f61054051011660440161051cfd5b6c064f964e68233a800000000012610480516020526000526040600020805461052051808203828111614a6f57905090508155506002546323b872dd610540526104a051610560523061058052610520516105a0526020610540606461055c6000855af1611c89573d600060003e3d6000fd5b60203d10614a6f57610540518060011c614a6f576105c0526105c090505115614a6f57610480516040526104e051606052610500516080526104e05160a0526105005160c052600460e052611cdc6138b6565b610480516104a0517fbd02ce585321c0e9e7b7c479d640b5e41c559c615f32449f741727c6162f4ac861052051610540526020610540a36003600155005b632bcbc3ef81186121995760643610614a6f576004358060a01c614a6f57610480526024358060a01c614a6f576104a05260443580600f0b8118614a6f576104c052600154600214614a6f5760026001556c064f964e68233a80000000000b54611de45760126104e0527f43757272656e746c792064697361626c65640000000000000000000000000000610500526104e0506104e0518061050001601f826000031636823750506308c379a06104a05260206104c052601f19601f6104e05101166044016104bcfd5b6c064f964e68233a80000000000f543318611e00576001611e1c565b6c064f964e68233a800000000010336020526000526040600020545b611eab5760236104e0527f50726f7879206e6f742077686974656c6973746564205b61646d696e206c6576610500527f656c5d0000000000000000000000000000000000000000000000000000000000610520526104e0506104e0518061050001601f826000031636823750506308c379a06104a05260206104c052601f19601f6104e05101166044016104bcfd5b6c064f964e68233a80000000001161048051602052600052604060002054331815611f5b5760246104e0527f50726f7879206e6f742077686974656c6973746564205b7374616b6572206c65610500527f76656c5d00000000000000000000000000000000000000000000000000000000610520526104e0506104e0518061050001601f826000031636823750506308c379a06104a05260206104c052601f19601f6104e05101166044016104bcfd5b600461048051602052600052604060002080546104e052600181015461050052506104e05160008112614a6f5761052052610500514210611ffc57601d610540527f4e6f207472616e73666572732061667465722065787069726174696f6e0000006105605261054050610540518061056001601f826000031636823750506308c379a061050052602061052052601f19601f61054051011660440161051cfd5b6104c05160008112614a6f57610540526c064f964e68233a800000000012610480516020526000526040600020805461054051808201828110614a6f5790509050815550610520516c064f964e68233a8000000000126104805160205260005260406000205411156120ce57601d610560527f416d6f756e742065786365656473206c6f636b65642062616c616e63650000006105805261056050610560518061058001601f826000031636823750506308c379a061052052602061054052601f19601f61056051011660440161053cfd5b60025463a9059cbb610560526104a05161058052610540516105a0526020610560604461057c6000855af1612108573d600060003e3d6000fd5b60203d10614a6f57610560518060011c614a6f576105c0526105c090505115614a6f57610480516040526104e051606052610500516080526104e05160a0526105005160c052600560e05261215b6138b6565b6104a051610480517f9b4385b7a76ef579e481bdac2c47bfd58d1bb8635d3898165499b2326f1f0f8a61054051610560526020610560a36003600155005b6370a0823181186121b55760243610614a6f57426060526121ce565b62fdd58e81186123125760443610614a6f576024356060525b6004358060a01c614a6f576040526c064f964e68233a80000000000760405160205260005260406000205460805260805161221557600060a052602060a061231056612310565b6c064f964e68233a80000000000660405160205260005260406000206005608051633b9ac9ff8111614a6f570281019050805460a052600181015460c052600281015460e0526003810154610100526004810154610120525060a05160c05160605160e051808203828111614a6f579050905080607f1c614a6f5780820280600f0b8118614a6f579050905080820380600f0b8118614a6f579050905060a0527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60a051136122e457600060a0525b60a05160008112614a6f57610140526101205161014051101561230a5761012051610140525b60206101405bf35b634ee2cd7e81186126c05760443610614a6f576004358060a01c614a6f57610100524360243511614a6f576000610120526c064f964e68233a800000000007610100516020526000526040600020546101405260006080905b806101605261014051610120511061238257612420565b6101205161014051808201828110614a6f579050905060018101818110614a6f5790508060011c9050610180526024356c064f964e68233a800000000006610100516020526000526040600020600561018051633b9ac9ff8111614a6f57028101905060038101905054111561240c576101805160018103818111614a6f57905061014052612415565b61018051610120525b60010181811861236b575b50506c064f964e68233a800000000006610100516020526000526040600020600561012051633b9ac9ff8111614a6f57028101905080546101605260018101546101805260028101546101a05260038101546101c05260048101546101e05250600554610200526024356040526102005160605261249f610240614825565b61024051610220526005610220516c01431e0fae6d7217ca9fffffff8111614a6f570260060180546102405260018101546102605260028101546102805260038101546102a05260048101546102c052506040366102e03761020051610220511061253757436102a051808203828111614a6f57905090506102e0524261028051808203828111614a6f5790509050610300526125c2565b60056102205160018101818110614a6f5790506c01431e0fae6d7217ca9fffffff8111614a6f570260060180546103205260018101546103405260028101546103605260038101546103805260048101546103a05250610380516102a051808203828111614a6f57905090506102e0526103605161028051808203828111614a6f5790509050610300525b61028051610320526102e051156126285761032051610300516024356102a051808203828111614a6f5790509050808202811583838304141715614a6f57905090506102e0518015614a6f5780820490509050808201828110614a6f5790509050610320525b6101605161018051610320516101a051808203828111614a6f579050905080607f1c614a6f5780820280600f0b8118614a6f579050905080820380600f0b8118614a6f579050905061016052600061016051121561268d5760006101e0511015612690565b60015b6126a85760006103405260206103406126be566126be565b6101605160008112614a6f576103405260206103405bf35b6318160ddd81186126dd5760043610614a6f5742610200526126f8565b63bd85b03981186127845760243610614a6f57600435610200525b600554610220526005610220516c01431e0fae6d7217ca9fffffff8111614a6f570260060180546102405260018101546102605260028101546102805260038101546102a05260048101546102c0525060206102405160405261026051606052610280516080526102a05160a0526102c05160c0526102005160e05261277f6102e06148d5565b6102e0f35b63981b24d081186129aa5760243610614a6f574360043511614a6f5760055461020052600435604052610200516060526127bf610240614825565b61024051610220526005610220516c01431e0fae6d7217ca9fffffff8111614a6f570260060180546102405260018101546102605260028101546102805260038101546102a05260048101546102c0525060006102e05261020051610220511061289257436102a0511461295c576004356102a051808203828111614a6f57905090504261028051808203828111614a6f5790509050808202811583838304141715614a6f5790509050436102a051808203828111614a6f57905090508015614a6f57808204905090506102e05261295c565b60056102205160018101818110614a6f5790506c01431e0fae6d7217ca9fffffff8111614a6f570260060180546103005260018101546103205260028101546103405260038101546103605260048101546103805250610360516102a0511461295c576004356102a051808203828111614a6f57905090506103405161028051808203828111614a6f5790509050808202811583838304141715614a6f5790509050610360516102a051808203828111614a6f57905090508015614a6f57808204905090506102e0525b60206102405160405261026051606052610280516080526102a05160a0526102c05160c052610280516102e051808201828110614a6f579050905060e0526129a56103006148d5565b610300f35b6347c7341e81186129c95760043610614a6f5760035460405260206040f35b630f4174a08118612a5c5760243610614a6f574360043511614a6f576005546101005260043560405261010051606052612a04610140614825565b61014051610120526005610120516c01431e0fae6d7217ca9fffffff8111614a6f570260060180546101405260018101546101605260028101546101805260038101546101a05260048101546101c0525060206101c0f35b6388c2b3e38118612aed5760043610614a6f576c064f964e68233a80000000001d543318614a6f576c064f964e68233a80000000000e54156c064f964e68233a80000000000e5560c036604037612ab16138b6565b7feab47e2228a51fa30e80a103a8e0105cb452653bb433513d6bf11e3e3f3637a36c064f964e68233a80000000000e54610480526020610480a1005b6316f616728118612b7e5760043610614a6f576c064f964e68233a80000000001d543318614a6f576c064f964e68233a80000000000954156c064f964e68233a8000000000095560c036604037612b426138b6565b7fdf5e6b6b37885e5046023bc28abaf4fa317db98e6c992dbba3830fd69f0f03d46c064f964e68233a80000000000954610480526020610480a1005b630b33ac488118612c0f5760043610614a6f576c064f964e68233a80000000001d543318614a6f576c064f964e68233a80000000000a54156c064f964e68233a80000000000a5560c036604037612bd36138b6565b7f924855749e6db89374eeb6d4f88b5c3f2c3f4efff48671af2f5ebab4a558faf66c064f964e68233a80000000000a54610480526020610480a1005b63e8ff7e828118612ca05760043610614a6f576c064f964e68233a80000000001d543318614a6f576c064f964e68233a80000000000b54156c064f964e68233a80000000000b5560c036604037612c646138b6565b7f2e0793e6a4f1baa7980928be0706d6246f765c4c1038e17ef46dd004504b65fa6c064f964e68233a80000000000b54610480526020610480a1005b63172cb9998118612d315760043610614a6f576c064f964e68233a80000000001d543318614a6f576c064f964e68233a80000000000c54156c064f964e68233a80000000000c5560c036604037612cf56138b6565b7f0e9338cb371255221e6df7c319bbd0290c0a544046aaf2d21b42ff796e6f71746c064f964e68233a80000000000c54610480526020610480a1005b630929f36f8118612dc25760043610614a6f576c064f964e68233a80000000001d543318614a6f576c064f964e68233a80000000000d54156c064f964e68233a80000000000d5560c036604037612d866138b6565b7f3f6a71816fba143945ad3184a7c46790c48e516749eb3cc736342f5bb3eb88cb6c064f964e68233a80000000000d54610480526020610480a1005b63bacec9678118612eb55760243610614a6f576004358060a01c614a6f576040526c064f964e68233a80000000001d54331815612e5657600a6060527f41646d696e206f6e6c790000000000000000000000000000000000000000000060805260605060605180608001601f826000031636823750506308c379a06020526020604052601f19601f6060510116604401603cfd5b6040516c064f964e68233a80000000000f5560016c064f964e68233a8000000000106040516020526000526040600020557f3c3597bbc034da275532ab551a8916a9bade58914700b6aa1ab96f16243bafaa60405160605260206060a1005b63417f25c98118612fd25760243610614a6f576004358060a01c614a6f576040526c064f964e68233a80000000001d54331815612f4957600a6060527f41646d696e206f6e6c790000000000000000000000000000000000000000000060805260605060605180608001601f826000031636823750506308c379a06020526020604052601f19601f6060510116604401603cfd5b6c064f964e68233a800000000010604051602052600052604060002054156c064f964e68233a8000000000106040516020526000526040600020557f94fd3c4f99e5feb7efd85143b048ed68169886a015308a163bfdc9b36a8188596040516060526c064f964e68233a80000000001060405160205260005260406000205460805260406060a1005b634d857d0c81186131645760243610614a6f576004358060a01c614a6f57604052604051613001576001613016565b6040516c064f964e68233a80000000000f5418155b61309b5760236060527f50726f7879206e6f742077686974656c6973746564205b61646d696e206c65766080527f656c5d000000000000000000000000000000000000000000000000000000000060a05260605060605180608001601f826000031636823750506308c379a06020526020604052601f19601f6060510116604401603cfd5b6c064f964e68233a80000000001233602052600052604060002054156131185760196060527f4f75747374616e64696e67204650495320696e2070726f78790000000000000060805260605060605180608001601f826000031636823750506308c379a06020526020604052601f19601f6060510116604401603cfd5b6040516c064f964e68233a800000000011336020526000526040600020557f0c5eabcbebbb87a106d2bdf51ee002efdd0f1b547dae5f9d6d0e28b15af217db60405160605260206060a1005b63fc0c546a81186131835760043610614a6f5760025460405260206040f35b63047fc9aa81186131a25760043610614a6f5760035460405260206040f35b63cbf9fe5f81186131e75760243610614a6f576004358060a01c614a6f5760405260046040516020526000526040600020805460605260018101546080525060406060f35b63900cf0cf81186132065760043610614a6f5760055460405260206040f35b63d1febfb981186132625760243610614a6f5760056004356c01431e0fae6d7217ca9fffffff8111614a6f5702600601805460405260018101546060526002810154608052600381015460a052600481015460c0525060a06040f35b6328d09d4781186132e05760443610614a6f576004358060a01c614a6f576040526c064f964e68233a80000000000660405160205260005260406000206005602435633b9ac9ff8111614a6f57028101905080546060526001810154608052600281015460a052600381015460c052600481015460e0525060a06060f35b63010ae75781186133275760243610614a6f576004358060a01c614a6f576040526c064f964e68233a80000000000760405160205260005260406000205460605260206060f35b637119748481186133605760243610614a6f576c064f964e68233a80000000000860043560205260005260406000205460405260206040f35b63702254a1811861338b5760043610614a6f576c064f964e68233a8000000000095460405260206040f35b63811d35ba81186133b65760043610614a6f576c064f964e68233a80000000000a5460405260206040f35b63a61f15c481186133e15760043610614a6f576c064f964e68233a80000000000b5460405260206040f35b6395957b57811861340c5760043610614a6f576c064f964e68233a80000000000c5460405260206040f35b632e67535d81186134375760043610614a6f576c064f964e68233a80000000000d5460405260206040f35b63f894648581186134625760043610614a6f576c064f964e68233a80000000000e5460405260206040f35b630a5de864811861348d5760043610614a6f576c064f964e68233a80000000000f5460405260206040f35b63d5a7505081186134d45760243610614a6f576004358060a01c614a6f576040526c064f964e68233a80000000001060405160205260005260406000205460605260206060f35b638d94781a811861351b5760243610614a6f576004358060a01c614a6f576040526c064f964e68233a80000000001160405160205260005260406000205460605260206060f35b63312afb0381186135625760243610614a6f576004358060a01c614a6f576040526c064f964e68233a80000000001260405160205260005260406000205460605260206060f35b6306fdde0381186135ff5760043610614a6f57602080604052806040016c064f964e68233a8000000000135480825260208201600082601f0160051c60028111614a6f5780156135d157905b806c064f964e68233a80000000001401548160051b8401526001018181186135ae575b505050508051806020830101601f82600003163682375050601f19601f825160200101169050810190506040f35b6395d89b41811861366f5760043610614a6f57602080604052806040016c064f964e68233a80000000001654808252602082016c064f964e68233a80000000001754815250508051806020830101601f82600003163682375050601f19601f825160200101169050810190506040f35b6354fd4d5081186136df5760043610614a6f57602080604052806040016c064f964e68233a80000000001854808252602082016c064f964e68233a80000000001954815250508051806020830101601f82600003163682375050601f19601f825160200101169050810190506040f35b63313ce567811861370a5760043610614a6f576c064f964e68233a80000000001a5460405260206040f35b638ff36fd181186137355760043610614a6f576c064f964e68233a80000000001b5460405260206040f35b637175d4f781186137605760043610614a6f576c064f964e68233a80000000001c5460405260206040f35b63f851a440811861378b5760043610614a6f576c064f964e68233a80000000001d5460405260206040f35b6317f7182a81186137b65760043610614a6f576c064f964e68233a80000000001e5460405260206040f35b505b60006000fd5b32604051146138b4576c064f964e68233a80000000001c54606052606051156138335760605163c23697a860805260405160a052602060806024609c6000855af161380e573d600060003e3d6000fd5b60203d10614a6f576080518060011c614a6f5760c05260c090505115613833576138b4565b60256080527f536d61727420636f6e7472616374206465706f7369746f7273206e6f7420616c60a0527f6c6f77656400000000000000000000000000000000000000000000000000000060c0526080506080518060a001601f826000031636823750506308c379a06040526020606052601f19601f6080510116604401605cfd5b565b61018036610100376005546102805260405115613a3a5742608051116138dd5760006138e5565b600160605112155b15613952576060516003810280600f0b8118614a6f579050630784ce0081059050610120526060516101205160805142808203828111614a6f579050905080607f1c614a6f5780820280600f0b8118614a6f579050905080820180600f0b8118614a6f5790509050610100525b4260c0511161396257600061396a565b600160a05112155b156139d75760a0516003810280600f0b8118614a6f579050630784ce00810590506101c05260a0516101c05160c05142808203828111614a6f579050905080607f1c614a6f5780820280600f0b8118614a6f579050905080820180600f0b8118614a6f57905090506101a0525b6c064f964e68233a8000000000086080516020526000526040600020546102405260c05115613a3a5760805160c05118613a18576102405161026052613a3a565b6c064f964e68233a80000000000860c051602052600052604060002054610260525b6040366102a037426102e05243610300526000610320526102805115613aa4576005610280516c01431e0fae6d7217ca9fffffff8111614a6f570260060180546102a05260018101546102c05260028101546102e052600381015461030052600481015461032052505b6102e051610340526102a051610360526102c051610380526102e0516103a052610300516103c052610320516103e0526000610400526102e051421115613b3b574361030051808203828111614a6f5790509050670de0b6b3a7640000810281670de0b6b3a7640000820418614a6f579050426102e051808203828111614a6f57905090508015614a6f5780820490509050610400525b6103405162093a808104905062093a8081028162093a80820418614a6f57905061042052600060ff905b80610440526104205162093a808101818110614a6f57905061042052600061046052426104205111613bb8576c064f964e68233a8000000000086104205160205260005260406000205461046052613bbe565b42610420525b6102a0516102c0516104205161034051808203828111614a6f579050905080607f1c614a6f5780820280600f0b8118614a6f579050905080820380600f0b8118614a6f57905090506102a0526102c0516104605180820180600f0b8118614a6f57905090506102c0527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6102a05113613c585760006102a0525b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6102c05113613c895760006102c0525b6104205161034052610420516102e0526103c05161040051610420516103a051808203828111614a6f5790509050808202811583838304141715614a6f5790509050670de0b6b3a764000081049050808201828110614a6f5790509050610300526102805160018101818110614a6f57905061028052426104205118613d17574361030052613d6c56613d61565b6005610280516c01431e0fae6d7217ca9fffffff8111614a6f57026006016102a05181556102c05160018201556102e0516002820155610300516003820155610320516004820155505b600101818118613b65575b50506102805160055560405115613f52576102c0516101c0516101205180820380600f0b8118614a6f579050905080820180600f0b8118614a6f57905090506102c0526102a0516101a0516101005180820380600f0b8118614a6f579050905080820180600f0b8118614a6f57905090506102a05260605160a0511315613e23576103205160a05160605180820380600f0b8118614a6f579050905060008112614a6f57808201828110614a6f5790509050610320525b60605160a0511215613ef0576103205160605160a05180820380600f0b8118614a6f579050905060008112614a6f57808203828111614a6f579050905061032052600760e05118613e78574260c05110613e7b565b60005b15613eb9576102a05160a05180820180600f0b8118614a6f57905090506102a0526102a05160605180820380600f0b8118614a6f57905090506102a0525b60a051613ef0576c064f964e68233a80000000000e54613ef0576102a05160605180820380600f0b8118614a6f57905090506102a0525b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6102c05113613f215760006102c0525b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6102a05113613f525760006102a0525b6005610280516c01431e0fae6d7217ca9fffffff8111614a6f57026006016102a05181556102c05160018201556102e051600282015561030051600382015561032051600482015550604051156141645742608051111561401557610240516101205180820180600f0b8118614a6f57905090506102405260805160c05118613ff357610240516101c05180820380600f0b8118614a6f5790509050610240525b610240516c064f964e68233a8000000000086080516020526000526040600020555b4260c051111561406a5760805160c051111561406a57610260516101c05180820380600f0b8118614a6f579050905061026052610260516c064f964e68233a80000000000860c0516020526000526040600020555b6c064f964e68233a80000000000760405160205260005260406000205460018101818110614a6f57905061044052610440516c064f964e68233a800000000007604051602052600052604060002055426101e0524361020052600460405160205260005260406000205460008112614a6f57610220524260c05110156141065760046040516020526000526040600020546101a05260006101c0525b6c064f964e68233a8000000000066040516020526000526040600020600561044051633b9ac9ff8111614a6f5702810190506101a05181556101c05160018201556101e0516002820155610200516003820155610220516004820155505b565b6002546323b872dd610560526104a05161058052306105a0526104c0516105c0526020610560606461057c6000855af16141a5573d600060003e3d6000fd5b60203d10614a6f57610560518060011c614a6f576105e0526105e090505115614a6f57610500516105605261052051610580526003546105a052610560516105c052610580516105e0526105a0516104c051808201828110614a6f57905090506003556105c0516104c05180607f1c614a6f5780820180600f0b8118614a6f57905090506105c0526104e0511561423f576104e0516105e0525b60046104805160205260005260406000206105c05181556105e0516001820155506104805160405261056051606052610580516080526105c05160a0526105e05160c0526105405160e0526142926138b6565b6105e0516104a051610480517f4ace3cb811d903eba44ce1721d1a1d79232246711977f44236000551f8c11cc16104c05161060052610540516106205242610640526060610600a47f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c6105a051610600526105a0516104c051808201828110614a6f5790509050610620526040610600a1565b6c064f964e68233a80000000000f546106805114614361576c064f964e68233a8000000000106106805160205260005260406000205415614364565b60005b1561437857610680516040526143786137be565b610660516040526143876137be565b600461066051602052600052604060002080546106c05260018101546106e052506106a051614416576011610700527f56616c7565206d757374206265203e20300000000000000000000000000000006107205261070050610700518061072001601f826000031636823750506308c379a06106c05260206106e052601f19601f6107005101166044016106dcfd5b60016106c0511215614488576016610700527f4e6f206578697374696e67206c6f636b20666f756e64000000000000000000006107205261070050610700518061072001601f826000031636823750506308c379a06106c05260206106e052601f19601f6107005101166044016106dcfd5b426106e0511161451d576024610700527f43616e6e6f742061646420746f2065787069726564206c6f636b2e2057697468610720527f64726177000000000000000000000000000000000000000000000000000000006107405261070050610700518061072001601f826000031636823750506308c379a06106c05260206106e052601f19601f6107005101166044016106dcfd5b6106605161048052610680516104a0526106a0516104c05260006104e0526106c051610500526106e05161052052600161054052614559614166565b565b600061050051121561456e576000614579565b6104c0516105005113155b614608576026610540527f43616e6e6f74207769746864726177206d6f7265207468616e20746865207573610560527f65722068617300000000000000000000000000000000000000000000000000006105805261054050610540518061056001601f826000031636823750506308c379a061050052602061052052601f19601f61054051011660440161051cfd5b6104c051610540526104e051610560526105005160008112614a6f5761058052610540516105a052610560516105c05261054051610500511861464c576000610560525b610540516105005180820380600f0b8118614a6f5790509050610540526004610480516020526000526040600020610540518155610560516001820155506003546105e0526105e05161058051808203828111614a6f5790509050600355610480516040526105a0516060526105c0516080526105405160a0526105605160c0526105205160e0526146dc6138b6565b60025463a9059cbb610600526104a0516106205261058051610640526020610600604461061c6000855af1614716573d600060003e3d6000fd5b60203d10614a6f57610600518060011c614a6f576106605261066090505161479e576019610680527f4552433230207472616e73666572206f7574206661696c6564000000000000006106a0526106805061068051806106a001601f826000031636823750506308c379a061064052602061066052601f19601f61068051011660440161065cfd5b6104a051610480517ff341246adaac6f497bc2a656f546ab9e182111d630394f0c57c710a59a2cb567610580516106005242610620526040610600a37f5e2aa66efd74cce82b21852e317e5490d9ecc9e6bb953ae24d90851258cc2f5c6105e051610600526105e05161058051808203828111614a6f5790509050610620526040610600a1565b600060805260605160a05260006080905b8060c05260a0516080511061484a576148cb565b60805160a051808201828110614a6f579050905060018101818110614a6f5790508060011c905060e052604051600560e0516c01431e0fae6d7217ca9fffffff8111614a6f57026006016003810190505411156148b95760e05160018103818111614a6f57905060a0526148c0565b60e0516080525b600101818118614836575b5050608051815250565b60405161010052606051610120526080516101405260a0516101605260c051610180526101405162093a808104905062093a8081028162093a80820418614a6f5790506101a052600060ff905b806101c0526101a05162093a808101818110614a6f5790506101a05260006101e05260e0516101a05111614977576c064f964e68233a8000000000086101a0516020526000526040600020546101e05261497f565b60e0516101a0525b61010051610120516101a05161014051808203828111614a6f579050905080607f1c614a6f5780820280600f0b8118614a6f579050905080820380600f0b8118614a6f57905090506101005260e0516101a051186149dc57614a0c565b610120516101e05180820180600f0b8118614a6f5790509050610120526101a05161014052600101818118614922575b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101005113614a3f576000610100525b6101005160008112614a6f576101c052610180516101c0511015614a6657610180516101c0525b6101c051815250565b600080fda165767970657283000307000b

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

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.