ETH Price: $3,751.58 (-1.83%)
Gas: 21 Gwei

Contract

0x503e0BaB6acDAE73eA7fb7cf6Ae5792014dbe935
 

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Value
Approve199320362024-05-23 10:38:236 days ago1716460703IN
0x503e0BaB...014dbe935
0 ETH0.000233269.4326712
Approve199320362024-05-23 10:38:236 days ago1716460703IN
0x503e0BaB...014dbe935
0 ETH0.000233269.4326712
Redeem199225362024-05-22 2:45:117 days ago1716345911IN
0x503e0BaB...014dbe935
0 ETH0.003857527.86238757
Redeem199071552024-05-19 23:06:599 days ago1716160019IN
0x503e0BaB...014dbe935
0 ETH0.00144832.99253664
Redeem198576282024-05-13 0:50:2316 days ago1715561423IN
0x503e0BaB...014dbe935
0 ETH0.000290973.30323177
Redeem198287372024-05-08 23:53:2320 days ago1715212403IN
0x503e0BaB...014dbe935
0 ETH0.001602013.62867829
Redeem197821192024-05-02 11:23:5927 days ago1714649039IN
0x503e0BaB...014dbe935
0 ETH0.003412158
Redeem197734982024-05-01 6:29:5928 days ago1714544999IN
0x503e0BaB...014dbe935
0 ETH0.00200374.59967491
Redeem197378062024-04-26 6:39:1133 days ago1714113551IN
0x503e0BaB...014dbe935
0 ETH0.003529467.99447455
Redeem197317842024-04-25 10:28:1134 days ago1714040891IN
0x503e0BaB...014dbe935
0 ETH0.0007561217
Redeem197317822024-04-25 10:27:4734 days ago1714040867IN
0x503e0BaB...014dbe935
0 ETH0.0097289617
Redeem197309622024-04-25 7:42:4734 days ago1714030967IN
0x503e0BaB...014dbe935
0 ETH0.004845079.97327821
Redeem196661462024-04-16 6:05:2343 days ago1713247523IN
0x503e0BaB...014dbe935
0 ETH0.003839588.69739894
Redeem196574442024-04-15 0:49:1144 days ago1713142151IN
0x503e0BaB...014dbe935
0 ETH0.000536257.55292806
Redeem196445422024-04-13 5:22:3546 days ago1712985755IN
0x503e0BaB...014dbe935
0 ETH0.0025318611.07713385
Redeem196377742024-04-12 6:35:5947 days ago1712903759IN
0x503e0BaB...014dbe935
0 ETH0.0050198511.80585878
Redeem196271292024-04-10 18:50:4748 days ago1712775047IN
0x503e0BaB...014dbe935
0 ETH0.0102122523.79435321
Redeem196241622024-04-10 8:53:1149 days ago1712739191IN
0x503e0BaB...014dbe935
0 ETH0.0057393413
Redeem196228872024-04-10 4:36:4749 days ago1712723807IN
0x503e0BaB...014dbe935
0 ETH0.0053645712.88359204
Redeem196132062024-04-08 20:04:1150 days ago1712606651IN
0x503e0BaB...014dbe935
0 ETH0.0152442734.5283676
Redeem196125452024-04-08 17:50:4750 days ago1712598647IN
0x503e0BaB...014dbe935
0 ETH0.0095989739.07500542
Redeem196062542024-04-07 20:40:5951 days ago1712522459IN
0x503e0BaB...014dbe935
0 ETH0.00717116.36094494
Approve196029222024-04-07 9:27:5952 days ago1712482079IN
0x503e0BaB...014dbe935
0 ETH0.0006177913.14090972
Deposit196028452024-04-07 9:12:1152 days ago1712481131IN
0x503e0BaB...014dbe935
0 ETH0.0013122513.58865831
Redeem196010762024-04-07 3:15:2352 days ago1712459723IN
0x503e0BaB...014dbe935
0 ETH0.0010053312.06721086
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To Value
192378842024-02-16 3:47:23103 days ago1708055243  Contract Creation0 ETH
Loading...
Loading

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

Contract Name:
Yearn V3 Vault

Compiler Version
vyper:0.3.7

Optimization Enabled:
N/A

Other Settings:
GNU AGPLv3 license

Contract Source Code (Vyper language format)

# @version 0.3.7

"""
@title Yearn V3 Vault
@license GNU AGPLv3
@author yearn.finance
@notice
    The Yearn VaultV3 is designed as an non-opinionated system to distribute funds of 
    depositors for a specific `asset` into different opportunities (aka Strategies)
    and manage accounting in a robust way.

    Depositors receive shares (aka vaults tokens) proportional to their deposit amount. 
    Vault tokens are yield-bearing and can be redeemed at any time to get back deposit 
    plus any yield generated.

    Addresses that are given different permissioned roles by the `role_manager` 
    are then able to allocate funds as they best see fit to different strategies 
    and adjust the strategies and allocations as needed, as well as reporting realized
    profits or losses.

    Strategies are any ERC-4626 compliant contracts that use the same underlying `asset` 
    as the vault. The vault provides no assurances as to the safety of any strategy
    and it is the responsibility of those that hold the corresponding roles to choose
    and fund strategies that best fit their desired specifications.

    Those holding vault tokens are able to redeem the tokens for the corresponding
    amount of underlying asset based on any reported profits or losses since their
    initial deposit.

    The vault is built to be customized by the management to be able to fit their
    specific desired needs Including the customization of strategies, accountants, 
    ownership etc.
"""

from vyper.interfaces import ERC20
from vyper.interfaces import ERC20Detailed

# INTERFACES #
interface IStrategy:
    def asset() -> address: view
    def balanceOf(owner: address) -> uint256: view
    def maxDeposit(receiver: address) -> uint256: view
    def redeem(shares: uint256, receiver: address, owner: address) -> uint256: nonpayable
    def deposit(assets: uint256, receiver: address) -> uint256: nonpayable
    def totalAssets() -> (uint256): view
    def convertToAssets(shares: uint256) -> uint256: view
    def convertToShares(assets: uint256) -> uint256: view
    def previewWithdraw(assets: uint256) -> uint256: view
    def maxRedeem(owner: address) -> uint256: view

interface IAccountant:
    def report(strategy: address, gain: uint256, loss: uint256) -> (uint256, uint256): nonpayable

interface IDepositLimitModule:
    def available_deposit_limit(receiver: address) -> uint256: view
    
interface IWithdrawLimitModule:
    def available_withdraw_limit(owner: address, max_loss: uint256, strategies: DynArray[address, MAX_QUEUE]) -> uint256: view

interface IFactory:
    def protocol_fee_config() -> (uint16, address): view

# EVENTS #
# ERC4626 EVENTS
event Deposit:
    sender: indexed(address)
    owner: indexed(address)
    assets: uint256
    shares: uint256

event Withdraw:
    sender: indexed(address)
    receiver: indexed(address)
    owner: indexed(address)
    assets: uint256
    shares: uint256

# ERC20 EVENTS
event Transfer:
    sender: indexed(address)
    receiver: indexed(address)
    value: uint256

event Approval:
    owner: indexed(address)
    spender: indexed(address)
    value: uint256

# STRATEGY EVENTS
event StrategyChanged:
    strategy: indexed(address)
    change_type: indexed(StrategyChangeType)
    
event StrategyReported:
    strategy: indexed(address)
    gain: uint256
    loss: uint256
    current_debt: uint256
    protocol_fees: uint256
    total_fees: uint256
    total_refunds: uint256

# DEBT MANAGEMENT EVENTS
event DebtUpdated:
    strategy: indexed(address)
    current_debt: uint256
    new_debt: uint256

# ROLE UPDATES
event RoleSet:
    account: indexed(address)
    role: indexed(Roles)

event RoleStatusChanged:
    role: indexed(Roles)
    status: indexed(RoleStatusChange)

# STORAGE MANAGEMENT EVENTS
event UpdateRoleManager:
    role_manager: indexed(address)

event UpdateAccountant:
    accountant: indexed(address)

event UpdateDepositLimitModule:
    deposit_limit_module: indexed(address)

event UpdateWithdrawLimitModule:
    withdraw_limit_module: indexed(address)

event UpdateDefaultQueue:
    new_default_queue: DynArray[address, MAX_QUEUE]

event UpdateUseDefaultQueue:
    use_default_queue: bool

event UpdatedMaxDebtForStrategy:
    sender: indexed(address)
    strategy: indexed(address)
    new_debt: uint256

event UpdateDepositLimit:
    deposit_limit: uint256

event UpdateMinimumTotalIdle:
    minimum_total_idle: uint256

event UpdateProfitMaxUnlockTime:
    profit_max_unlock_time: uint256

event DebtPurchased:
    strategy: indexed(address)
    amount: uint256

event Shutdown:
    pass

# STRUCTS #
struct StrategyParams:
    # Timestamp when the strategy was added.
    activation: uint256 
    # Timestamp of the strategies last report.
    last_report: uint256
    # The current assets the strategy holds.
    current_debt: uint256
    # The max assets the strategy can hold. 
    max_debt: uint256

# CONSTANTS #
# The max length the withdrawal queue can be.
MAX_QUEUE: constant(uint256) = 10
# 100% in Basis Points.
MAX_BPS: constant(uint256) = 10_000
# Extended for profit locking calculations.
MAX_BPS_EXTENDED: constant(uint256) = 1_000_000_000_000
# The version of this vault.
API_VERSION: constant(String[28]) = "3.0.1"

# ENUMS #
# Each permissioned function has its own Role.
# Roles can be combined in any combination or all kept separate.
# Follows python Enum patterns so the first Enum == 1 and doubles each time.
enum Roles:
    ADD_STRATEGY_MANAGER # Can add strategies to the vault.
    REVOKE_STRATEGY_MANAGER # Can remove strategies from the vault.
    FORCE_REVOKE_MANAGER # Can force remove a strategy causing a loss.
    ACCOUNTANT_MANAGER # Can set the accountant that assess fees.
    QUEUE_MANAGER # Can set the default withdrawal queue.
    REPORTING_MANAGER # Calls report for strategies.
    DEBT_MANAGER # Adds and removes debt from strategies.
    MAX_DEBT_MANAGER # Can set the max debt for a strategy.
    DEPOSIT_LIMIT_MANAGER # Sets deposit limit and module for the vault.
    WITHDRAW_LIMIT_MANAGER # Sets the withdraw limit module.
    MINIMUM_IDLE_MANAGER # Sets the minimum total idle the vault should keep.
    PROFIT_UNLOCK_MANAGER # Sets the profit_max_unlock_time.
    DEBT_PURCHASER # Can purchase bad debt from the vault.
    EMERGENCY_MANAGER # Can shutdown vault in an emergency.

enum StrategyChangeType:
    ADDED
    REVOKED

enum Rounding:
    ROUND_DOWN
    ROUND_UP

enum RoleStatusChange:
    OPENED
    CLOSED

# IMMUTABLE #
# Underlying token used by the vault.
ASSET: immutable(ERC20)
# Based off the `asset` decimals.
DECIMALS: immutable(uint256)
# Deployer contract used to retrieve the protocol fee config.
FACTORY: public(immutable(address))

# STORAGE #
# HashMap that records all the strategies that are allowed to receive assets from the vault.
strategies: public(HashMap[address, StrategyParams])
# The current default withdrawal queue.
default_queue: public(DynArray[address, MAX_QUEUE])
# Should the vault use the default_queue regardless whats passed in.
use_default_queue: public(bool)

# ERC20 - amount of shares per account
balance_of: HashMap[address, uint256]
# ERC20 - owner -> (spender -> amount)
allowance: public(HashMap[address, HashMap[address, uint256]])
# Total amount of shares that are currently minted including those locked.
# NOTE: To get the ERC20 compliant version user totalSupply().
total_supply: public(uint256)

# Total amount of assets that has been deposited in strategies.
total_debt: uint256
# Current assets held in the vault contract. Replacing balanceOf(this) to avoid price_per_share manipulation.
total_idle: uint256
# Minimum amount of assets that should be kept in the vault contract to allow for fast, cheap redeems.
minimum_total_idle: public(uint256)
# Maximum amount of tokens that the vault can accept. If totalAssets > deposit_limit, deposits will revert.
deposit_limit: public(uint256)
# Contract that charges fees and can give refunds.
accountant: public(address)
# Contract to control the deposit limit.
deposit_limit_module: public(address)
# Contract to control the withdraw limit.
withdraw_limit_module: public(address)
# HashMap mapping addresses to their roles
roles: public(HashMap[address, Roles])
# HashMap mapping roles to their permissioned state. If false, the role is not open to the public.
open_roles: public(HashMap[Roles, bool])
# Address that can add and remove roles to addresses.
role_manager: public(address)
# Temporary variable to store the address of the next role_manager until the role is accepted.
future_role_manager: public(address)

# ERC20 - name of the vaults token.
name: public(String[64])
# ERC20 - symbol of the vaults token.
symbol: public(String[32])

# State of the vault - if set to true, only withdrawals will be available. It can't be reverted.
shutdown: bool
# The amount of time profits will unlock over.
profit_max_unlock_time: uint256
# The timestamp of when the current unlocking period ends.
full_profit_unlock_date: uint256
# The per second rate at which profit will unlock.
profit_unlocking_rate: uint256
# Last timestamp of the most recent profitable report.
last_profit_update: uint256

# `nonces` track `permit` approvals with signature.
nonces: public(HashMap[address, uint256])
DOMAIN_TYPE_HASH: constant(bytes32) = keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)')
PERMIT_TYPE_HASH: constant(bytes32) = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")

# Constructor
@external
def __init__(
    asset: ERC20, 
    name: String[64], 
    symbol: String[32], 
    role_manager: address, 
    profit_max_unlock_time: uint256
):
    """
    @notice
        The constructor for the vault. Sets the asset, name, symbol, and role manager.
    @param asset
        The address of the asset that the vault will accept.
    @param name
        The name of the vault token.
    @param symbol
        The symbol of the vault token.
    @param role_manager 
        The address that can add and remove roles to addresses
    @param profit_max_unlock_time
        The amount of time that the profit will be locked for
    """
    ASSET = asset
    DECIMALS = convert(ERC20Detailed(asset.address).decimals(), uint256)
    assert DECIMALS < 256 # dev: see VVE-2020-0001
    
    FACTORY = msg.sender

    # Must be less than one year for report cycles
    assert profit_max_unlock_time <= 31_556_952 # dev: profit unlock time too long
    self.profit_max_unlock_time = profit_max_unlock_time

    self.name = name
    self.symbol = symbol
    self.role_manager = role_manager

## SHARE MANAGEMENT ##
## ERC20 ##
@internal
def _spend_allowance(owner: address, spender: address, amount: uint256):
    # Unlimited approval does nothing (saves an SSTORE)
    current_allowance: uint256 = self.allowance[owner][spender]
    if (current_allowance < max_value(uint256)):
        assert current_allowance >= amount, "insufficient allowance"
        self._approve(owner, spender, unsafe_sub(current_allowance, amount))

@internal
def _transfer(sender: address, receiver: address, amount: uint256):
    sender_balance: uint256 = self.balance_of[sender]
    assert sender_balance >= amount, "insufficient funds"
    self.balance_of[sender] = unsafe_sub(sender_balance, amount)
    self.balance_of[receiver] = unsafe_add(self.balance_of[receiver], amount)
    log Transfer(sender, receiver, amount)

@internal
def _transfer_from(sender: address, receiver: address, amount: uint256) -> bool:
    self._spend_allowance(sender, msg.sender, amount)
    self._transfer(sender, receiver, amount)
    return True

@internal
def _approve(owner: address, spender: address, amount: uint256) -> bool:
    self.allowance[owner][spender] = amount
    log Approval(owner, spender, amount)
    return True

@internal
def _increase_allowance(owner: address, spender: address, amount: uint256) -> bool:
    new_allowance: uint256 = self.allowance[owner][spender] + amount
    self.allowance[owner][spender] = new_allowance
    log Approval(owner, spender, new_allowance)
    return True

@internal
def _decrease_allowance(owner: address, spender: address, amount: uint256) -> bool:
    new_allowance: uint256 = self.allowance[owner][spender] - amount
    self.allowance[owner][spender] = new_allowance
    log Approval(owner, spender, new_allowance)
    return True

@internal
def _permit(
    owner: address, 
    spender: address, 
    amount: uint256, 
    deadline: uint256, 
    v: uint8, 
    r: bytes32, 
    s: bytes32
) -> bool:
    assert owner != empty(address), "invalid owner"
    assert deadline >= block.timestamp, "permit expired"
    nonce: uint256 = self.nonces[owner]
    digest: bytes32 = keccak256(
        concat(
            b'\x19\x01',
            self.domain_separator(),
            keccak256(
                concat(
                    PERMIT_TYPE_HASH,
                    convert(owner, bytes32),
                    convert(spender, bytes32),
                    convert(amount, bytes32),
                    convert(nonce, bytes32),
                    convert(deadline, bytes32),
                )
            )
        )
    )
    assert ecrecover(
        digest, convert(v, uint256), convert(r, uint256), convert(s, uint256)
    ) == owner, "invalid signature"

    self.allowance[owner][spender] = amount
    self.nonces[owner] = nonce + 1
    log Approval(owner, spender, amount)
    return True

@internal
def _burn_shares(shares: uint256, owner: address):
    self.balance_of[owner] -= shares
    self.total_supply = unsafe_sub(self.total_supply, shares)
    log Transfer(owner, empty(address), shares)

@view
@internal
def _unlocked_shares() -> uint256:
    """
    Returns the amount of shares that have been unlocked.
    To avoid sudden price_per_share spikes, profits must be processed 
    through an unlocking period. The mechanism involves shares to be 
    minted to the vault which are unlocked gradually over time. Shares 
    that have been locked are gradually unlocked over profit_max_unlock_time.
    """
    _full_profit_unlock_date: uint256 = self.full_profit_unlock_date
    unlocked_shares: uint256 = 0
    if _full_profit_unlock_date > block.timestamp:
        # If we have not fully unlocked, we need to calculate how much has been.
        unlocked_shares = self.profit_unlocking_rate * (block.timestamp - self.last_profit_update) / MAX_BPS_EXTENDED

    elif _full_profit_unlock_date != 0:
        # All shares have been unlocked
        unlocked_shares = self.balance_of[self]

    return unlocked_shares


@view
@internal
def _total_supply() -> uint256:
    # Need to account for the shares issued to the vault that have unlocked.
    return self.total_supply - self._unlocked_shares()

@internal
def _burn_unlocked_shares():
    """
    Burns shares that have been unlocked since last update. 
    In case the full unlocking period has passed, it stops the unlocking.
    """
    # Get the amount of shares that have unlocked
    unlocked_shares: uint256 = self._unlocked_shares()

    # IF 0 there's nothing to do.
    if unlocked_shares == 0:
        return

    # Only do an SSTORE if necessary
    if self.full_profit_unlock_date > block.timestamp:
        self.last_profit_update = block.timestamp

    # Burn the shares unlocked.
    self._burn_shares(unlocked_shares, self)

@view
@internal
def _total_assets() -> uint256:
    """
    Total amount of assets that are in the vault and in the strategies. 
    """
    return self.total_idle + self.total_debt

@view
@internal
def _convert_to_assets(shares: uint256, rounding: Rounding) -> uint256:
    """ 
    assets = shares * (total_assets / total_supply) --- (== price_per_share * shares)
    """
    if shares == max_value(uint256) or shares == 0:
        return shares

    total_supply: uint256 = self._total_supply()
    # if total_supply is 0, price_per_share is 1
    if total_supply == 0: 
        return shares

    numerator: uint256 = shares * self._total_assets()
    amount: uint256 = numerator / total_supply
    if rounding == Rounding.ROUND_UP and numerator % total_supply != 0:
        amount += 1

    return amount

@view
@internal
def _convert_to_shares(assets: uint256, rounding: Rounding) -> uint256:
    """
    shares = amount * (total_supply / total_assets) --- (== amount / price_per_share)
    """
    if assets == max_value(uint256) or assets == 0:
        return assets

    total_supply: uint256 = self._total_supply()
    total_assets: uint256 = self._total_assets()

    if total_assets == 0:
        # if total_assets and total_supply is 0, price_per_share is 1
        if total_supply == 0:
            return assets
        else:
            # Else if total_supply > 0 price_per_share is 0
            return 0

    numerator: uint256 = assets * total_supply
    shares: uint256 = numerator / total_assets
    if rounding == Rounding.ROUND_UP and numerator % total_assets != 0:
        shares += 1

    return shares

@internal
def _erc20_safe_approve(token: address, spender: address, amount: uint256):
    # Used only to approve tokens that are not the type managed by this Vault.
    # Used to handle non-compliant tokens like USDT
    assert ERC20(token).approve(spender, amount, default_return_value=True), "approval failed"

@internal
def _erc20_safe_transfer_from(token: address, sender: address, receiver: address, amount: uint256):
    # Used only to transfer tokens that are not the type managed by this Vault.
    # Used to handle non-compliant tokens like USDT
    assert ERC20(token).transferFrom(sender, receiver, amount, default_return_value=True), "transfer failed"

@internal
def _erc20_safe_transfer(token: address, receiver: address, amount: uint256):
    # Used only to send tokens that are not the type managed by this Vault.
    # Used to handle non-compliant tokens like USDT
    assert ERC20(token).transfer(receiver, amount, default_return_value=True), "transfer failed"

@internal
def _issue_shares(shares: uint256, recipient: address):
    self.balance_of[recipient] = unsafe_add(self.balance_of[recipient], shares)
    self.total_supply += shares

    log Transfer(empty(address), recipient, shares)

@internal
def _issue_shares_for_amount(amount: uint256, recipient: address) -> uint256:
    """
    Issues shares that are worth 'amount' in the underlying token (asset).
    WARNING: this takes into account that any new assets have been summed 
    to total_assets (otherwise pps will go down).
    """
    total_supply: uint256 = self._total_supply()
    total_assets: uint256 = self._total_assets()
    new_shares: uint256 = 0
    
    # If no supply PPS = 1.
    if total_supply == 0:
        new_shares = amount
    elif total_assets > amount:
        new_shares = amount * total_supply / (total_assets - amount)
    else:
        # If total_supply > 0 but amount = totalAssets we want to revert because
        # after first deposit, getting here would mean that the rest of the shares
        # would be diluted to a price_per_share of 0. Issuing shares would then mean
        # either the new depositor or the previous depositors will loose money.
        assert total_assets > amount, "amount too high"
  
    # We don't make the function revert
    if new_shares == 0:
       return 0

    self._issue_shares(new_shares, recipient)

    return new_shares

## ERC4626 ##
@view
@internal
def _max_deposit(receiver: address) -> uint256: 
    if receiver in [empty(address), self]:
        return 0

    # If there is a deposit limit module set use that.
    deposit_limit_module: address = self.deposit_limit_module
    if deposit_limit_module != empty(address):
        return IDepositLimitModule(deposit_limit_module).available_deposit_limit(receiver)
    
    # Else use the standard flow.
    _total_assets: uint256 = self._total_assets()
    _deposit_limit: uint256 = self.deposit_limit
    if (_total_assets >= _deposit_limit):
        return 0

    return unsafe_sub(_deposit_limit, _total_assets)

@view
@internal
def _max_withdraw(
    owner: address,
    max_loss: uint256,
    strategies: DynArray[address, MAX_QUEUE]
) -> uint256:
    """
    @dev Returns the max amount of `asset` an `owner` can withdraw.

    This will do a full simulation of the withdraw in order to determine
    how much is currently liquid and if the `max_loss` would allow for the 
    tx to not revert.

    This will track any expected loss to check if the tx will revert, but
    not account for it in the amount returned since it is unrealised and 
    therefore will not be accounted for in the conversion rates.

    i.e. If we have 100 debt and 10 of unrealised loss, the max we can get
    out is 90, but a user of the vault will need to call withdraw with 100
    in order to get the full 90 out.
    """

    # Get the max amount for the owner if fully liquid.
    max_assets: uint256 = self._convert_to_assets(self.balance_of[owner], Rounding.ROUND_DOWN)

    # If there is a withdraw limit module use that.
    withdraw_limit_module: address = self.withdraw_limit_module
    if withdraw_limit_module != empty(address):
        return min(
            # Use the min between the returned value and the max.
            # Means the limit module doesn't need to account for balances or conversions.
            IWithdrawLimitModule(withdraw_limit_module).available_withdraw_limit(owner, max_loss, strategies),
            max_assets
        )
    
    # See if we have enough idle to service the withdraw.
    current_idle: uint256 = self.total_idle
    if max_assets > current_idle:
        # Track how much we can pull.
        have: uint256 = current_idle
        loss: uint256 = 0

        # Cache the default queue.
        _strategies: DynArray[address, MAX_QUEUE] = self.default_queue

        # If a custom queue was passed, and we don't force the default queue.
        if len(strategies) != 0 and not self.use_default_queue:
            # Use the custom queue.
            _strategies = strategies

        for strategy in _strategies:
            # Can't use an invalid strategy.
            assert self.strategies[strategy].activation != 0, "inactive strategy"

            # Get the maximum amount the vault would withdraw from the strategy.
            to_withdraw: uint256 = min(
                # What we still need for the full withdraw.
                max_assets - have, 
                # The current debt the strategy has.
                self.strategies[strategy].current_debt
            )

            # Get any unrealised loss for the strategy.
            unrealised_loss: uint256 = self._assess_share_of_unrealised_losses(strategy, to_withdraw)

            # See if any limit is enforced by the strategy.
            strategy_limit: uint256 = IStrategy(strategy).convertToAssets(
                IStrategy(strategy).maxRedeem(self)
            )

            # Adjust accordingly if there is a max withdraw limit.
            if strategy_limit < to_withdraw - unrealised_loss:
                # lower unrealised loss to the proportional to the limit.
                unrealised_loss = unrealised_loss * strategy_limit / to_withdraw
                # Still count the unrealised loss as withdrawable.
                to_withdraw = strategy_limit + unrealised_loss

            # If 0 move on to the next strategy.
            if to_withdraw == 0:
                continue

            # If there would be a loss with a non-maximum `max_loss` value.
            if unrealised_loss > 0 and max_loss < MAX_BPS:
                # Check if the loss is greater than the allowed range.
                if loss + unrealised_loss > (have + to_withdraw) * max_loss / MAX_BPS:
                    # If so use the amounts up till now.
                    break

            # Add to what we can pull.
            have += to_withdraw

            # If we have all we need break.
            if have >= max_assets:
                break

            # Add any unrealised loss to the total
            loss += unrealised_loss

        # Update the max after going through the queue.
        # In case we broke early or exhausted the queue.
        max_assets = have

    return max_assets

@internal
def _deposit(sender: address, recipient: address, assets: uint256) -> uint256:
    """
    Used for `deposit` calls to transfer the amount of `asset` to the vault, 
    issue the corresponding shares to the `recipient` and update all needed 
    vault accounting.
    """
    assert self.shutdown == False # dev: shutdown
    assert assets <= self._max_deposit(recipient), "exceed deposit limit"
 
    # Transfer the tokens to the vault first.
    self._erc20_safe_transfer_from(ASSET.address, msg.sender, self, assets)
    # Record the change in total assets.
    self.total_idle += assets
    
    # Issue the corresponding shares for assets.
    shares: uint256 = self._issue_shares_for_amount(assets, recipient)

    assert shares > 0, "cannot mint zero"

    log Deposit(sender, recipient, assets, shares)
    return shares

@internal
def _mint(sender: address, recipient: address, shares: uint256) -> uint256:
    """
    Used for `mint` calls to issue the corresponding shares to the `recipient`,
    transfer the amount of `asset` to the vault, and update all needed vault 
    accounting.
    """
    assert self.shutdown == False # dev: shutdown
    # Get corresponding amount of assets.
    assets: uint256 = self._convert_to_assets(shares, Rounding.ROUND_UP)

    assert assets > 0, "cannot deposit zero"
    assert assets <= self._max_deposit(recipient), "exceed deposit limit"

    # Transfer the tokens to the vault first.
    self._erc20_safe_transfer_from(ASSET.address, msg.sender, self, assets)
    # Record the change in total assets.
    self.total_idle += assets
    
    # Issue the corresponding shares for assets.
    self._issue_shares(shares, recipient)

    log Deposit(sender, recipient, assets, shares)
    return assets

@view
@internal
def _assess_share_of_unrealised_losses(strategy: address, assets_needed: uint256) -> uint256:
    """
    Returns the share of losses that a user would take if withdrawing from this strategy
    e.g. if the strategy has unrealised losses for 10% of its current debt and the user 
    wants to withdraw 1000 tokens, the losses that he will take are 100 token
    """
    # Minimum of how much debt the debt should be worth.
    strategy_current_debt: uint256 = self.strategies[strategy].current_debt
    # The actual amount that the debt is currently worth.
    vault_shares: uint256 = IStrategy(strategy).balanceOf(self)
    strategy_assets: uint256 = IStrategy(strategy).convertToAssets(vault_shares)
    
    # If no losses, return 0
    if strategy_assets >= strategy_current_debt or strategy_current_debt == 0:
        return 0

    # Users will withdraw assets_to_withdraw divided by loss ratio (strategy_assets / strategy_current_debt - 1),
    # but will only receive assets_to_withdraw.
    # NOTE: If there are unrealised losses, the user will take his share.
    numerator: uint256 = assets_needed * strategy_assets
    losses_user_share: uint256 = assets_needed - numerator / strategy_current_debt
    # Always round up.
    if numerator % strategy_current_debt != 0:
        losses_user_share += 1

    return losses_user_share

@internal
def _withdraw_from_strategy(strategy: address, assets_to_withdraw: uint256):
    """
    This takes the amount denominated in asset and performs a {redeem}
    with the corresponding amount of shares.

    We use {redeem} to natively take on losses without additional non-4626 standard parameters.
    """
    # Need to get shares since we use redeem to be able to take on losses.
    shares_to_redeem: uint256 = min(
        # Use previewWithdraw since it should round up.
        IStrategy(strategy).previewWithdraw(assets_to_withdraw), 
        # And check against our actual balance.
        IStrategy(strategy).balanceOf(self)
    )
    # Redeem the shares.
    IStrategy(strategy).redeem(shares_to_redeem, self, self)

@internal
def _redeem(
    sender: address, 
    receiver: address, 
    owner: address,
    assets: uint256,
    shares_to_burn: uint256, 
    max_loss: uint256,
    strategies: DynArray[address, MAX_QUEUE]
) -> uint256:
    """
    This will attempt to free up the full amount of assets equivalent to
    `shares_to_burn` and transfer them to the `receiver`. If the vault does
    not have enough idle funds it will go through any strategies provided by
    either the withdrawer or the queue_manager to free up enough funds to 
    service the request.

    The vault will attempt to account for any unrealized losses taken on from
    strategies since their respective last reports.

    Any losses realized during the withdraw from a strategy will be passed on
    to the user that is redeeming their vault shares.
    """
    assert receiver != empty(address), "ZERO ADDRESS"
    assert max_loss <= MAX_BPS, "max loss"

    # If there is a withdraw limit module, check the max.
    if self.withdraw_limit_module != empty(address):
        assert assets <= self._max_withdraw(owner, max_loss, strategies), "exceed withdraw limit"

    shares: uint256 = shares_to_burn
    shares_balance: uint256 = self.balance_of[owner]

    assert shares > 0, "no shares to redeem"
    assert shares_balance >= shares, "insufficient shares to redeem"
    
    if sender != owner:
        self._spend_allowance(owner, sender, shares_to_burn)

    # The amount of the underlying token to withdraw.
    requested_assets: uint256 = assets

    # load to memory to save gas
    curr_total_idle: uint256 = self.total_idle
    
    # If there are not enough assets in the Vault contract, we try to free
    # funds from strategies.
    if requested_assets > curr_total_idle:

        # Cache the default queue.
        _strategies: DynArray[address, MAX_QUEUE] = self.default_queue

        # If a custom queue was passed, and we don't force the default queue.
        if len(strategies) != 0 and not self.use_default_queue:
            # Use the custom queue.
            _strategies = strategies

        # load to memory to save gas
        curr_total_debt: uint256 = self.total_debt

        # Withdraw from strategies only what idle doesn't cover.
        # `assets_needed` is the total amount we need to fill the request.
        assets_needed: uint256 = unsafe_sub(requested_assets, curr_total_idle)
        # `assets_to_withdraw` is the amount to request from the current strategy.
        assets_to_withdraw: uint256 = 0

        # To compare against real withdrawals from strategies
        previous_balance: uint256 = ASSET.balanceOf(self)

        for strategy in _strategies:
            # Make sure we have a valid strategy.
            assert self.strategies[strategy].activation != 0, "inactive strategy"

            # How much should the strategy have.
            current_debt: uint256 = self.strategies[strategy].current_debt

            # What is the max amount to withdraw from this strategy.
            assets_to_withdraw = min(assets_needed, current_debt)

            # Cache max_withdraw now for use if unrealized loss > 0
            # Use maxRedeem and convert since we use redeem.
            max_withdraw: uint256 = IStrategy(strategy).convertToAssets(
                IStrategy(strategy).maxRedeem(self)
            )

            # CHECK FOR UNREALISED LOSSES
            # If unrealised losses > 0, then the user will take the proportional share 
            # and realize it (required to avoid users withdrawing from lossy strategies).
            # NOTE: strategies need to manage the fact that realising part of the loss can 
            # mean the realisation of 100% of the loss!! (i.e. if for withdrawing 10% of the
            # strategy it needs to unwind the whole position, generated losses might be bigger)
            unrealised_losses_share: uint256 = self._assess_share_of_unrealised_losses(strategy, assets_to_withdraw)
            if unrealised_losses_share > 0:
                # If max withdraw is limiting the amount to pull, we need to adjust the portion of 
                # the unrealized loss the user should take.
                if max_withdraw < assets_to_withdraw - unrealised_losses_share:
                    # How much would we want to withdraw
                    wanted: uint256 = assets_to_withdraw - unrealised_losses_share
                    # Get the proportion of unrealised comparing what we want vs. what we can get
                    unrealised_losses_share = unrealised_losses_share * max_withdraw / wanted
                    # Adjust assets_to_withdraw so all future calculations work correctly
                    assets_to_withdraw = max_withdraw + unrealised_losses_share
                
                # User now "needs" less assets to be unlocked (as he took some as losses)
                assets_to_withdraw -= unrealised_losses_share
                requested_assets -= unrealised_losses_share
                # NOTE: done here instead of waiting for regular update of these values 
                # because it's a rare case (so we can save minor amounts of gas)
                assets_needed -= unrealised_losses_share
                curr_total_debt -= unrealised_losses_share

                # If max withdraw is 0 and unrealised loss is still > 0 then the strategy likely
                # realized a 100% loss and we will need to realize that loss before moving on.
                if max_withdraw == 0 and unrealised_losses_share > 0:
                    # Adjust the strategy debt accordingly.
                    new_debt: uint256 = current_debt - unrealised_losses_share
        
                    # Update strategies storage
                    self.strategies[strategy].current_debt = new_debt
                    # Log the debt update
                    log DebtUpdated(strategy, current_debt, new_debt)

            # Adjust based on the max withdraw of the strategy.
            assets_to_withdraw = min(assets_to_withdraw, max_withdraw)

            # Can't withdraw 0.
            if assets_to_withdraw == 0:
                continue
            
            # WITHDRAW FROM STRATEGY
            self._withdraw_from_strategy(strategy, assets_to_withdraw)
            post_balance: uint256 = ASSET.balanceOf(self)
            
            # Always check withdrawn against the real amounts.
            withdrawn: uint256 = post_balance - previous_balance
            loss: uint256 = 0
            # Check if we redeemed too much.
            if withdrawn > assets_to_withdraw:
                # Make sure we don't underflow in debt updates.
                if withdrawn > current_debt:
                    # Can't withdraw more than our debt.
                    assets_to_withdraw = current_debt
                else:
                    # Add the extra to how much we withdrew.
                    assets_to_withdraw += (unsafe_sub(withdrawn, assets_to_withdraw))

            # If we have not received what we expected, we consider the difference a loss.
            elif withdrawn < assets_to_withdraw:
                loss = unsafe_sub(assets_to_withdraw, withdrawn)

            # NOTE: strategy's debt decreases by the full amount but the total idle increases 
            # by the actual amount only (as the difference is considered lost).
            curr_total_idle += (assets_to_withdraw - loss)
            requested_assets -= loss
            curr_total_debt -= assets_to_withdraw

            # Vault will reduce debt because the unrealised loss has been taken by user
            new_debt: uint256 = current_debt - (assets_to_withdraw + unrealised_losses_share)
        
            # Update strategies storage
            self.strategies[strategy].current_debt = new_debt
            # Log the debt update
            log DebtUpdated(strategy, current_debt, new_debt)

            # Break if we have enough total idle to serve initial request.
            if requested_assets <= curr_total_idle:
                break

            # We update the previous_balance variable here to save gas in next iteration.
            previous_balance = post_balance

            # Reduce what we still need. Safe to use assets_to_withdraw 
            # here since it has been checked against requested_assets
            assets_needed -= assets_to_withdraw

        # If we exhaust the queue and still have insufficient total idle, revert.
        assert curr_total_idle >= requested_assets, "insufficient assets in vault"
        # Commit memory to storage.
        self.total_debt = curr_total_debt

    # Check if there is a loss and a non-default value was set.
    if assets > requested_assets and max_loss < MAX_BPS:
        # Assure the loss is within the allowed range.
        assert assets - requested_assets <= assets * max_loss / MAX_BPS, "too much loss"

    # First burn the corresponding shares from the redeemer.
    self._burn_shares(shares, owner)
    # Commit memory to storage.
    self.total_idle = curr_total_idle - requested_assets
    # Transfer the requested amount to the receiver.
    self._erc20_safe_transfer(ASSET.address, receiver, requested_assets)

    log Withdraw(sender, receiver, owner, requested_assets, shares)
    return requested_assets

## STRATEGY MANAGEMENT ##
@internal
def _add_strategy(new_strategy: address):
    assert new_strategy not in [self, empty(address)], "strategy cannot be zero address"
    assert IStrategy(new_strategy).asset() == ASSET.address, "invalid asset"
    assert self.strategies[new_strategy].activation == 0, "strategy already active"

    # Add the new strategy to the mapping.
    self.strategies[new_strategy] = StrategyParams({
        activation: block.timestamp,
        last_report: block.timestamp,
        current_debt: 0,
        max_debt: 0
    })

    # If the default queue has space, add the strategy.
    if len(self.default_queue) < MAX_QUEUE:
        self.default_queue.append(new_strategy)        
        
    log StrategyChanged(new_strategy, StrategyChangeType.ADDED)

@internal
def _revoke_strategy(strategy: address, force: bool=False):
    assert self.strategies[strategy].activation != 0, "strategy not active"

    # If force revoking a strategy, it will cause a loss.
    loss: uint256 = 0
    
    if self.strategies[strategy].current_debt != 0:
        assert force, "strategy has debt"
        # Vault realizes the full loss of outstanding debt.
        loss = self.strategies[strategy].current_debt
        # Adjust total vault debt.
        self.total_debt -= loss

        log StrategyReported(strategy, 0, loss, 0, 0, 0, 0)

    # Set strategy params all back to 0 (WARNING: it can be re-added).
    self.strategies[strategy] = StrategyParams({
      activation: 0,
      last_report: 0,
      current_debt: 0,
      max_debt: 0
    })

    # Remove strategy if it is in the default queue.
    new_queue: DynArray[address, MAX_QUEUE] = []
    for _strategy in self.default_queue:
        # Add all strategies to the new queue besides the one revoked.
        if _strategy != strategy:
            new_queue.append(_strategy)
        
    # Set the default queue to our updated queue.
    self.default_queue = new_queue

    log StrategyChanged(strategy, StrategyChangeType.REVOKED)

# DEBT MANAGEMENT #
@internal
def _update_debt(strategy: address, target_debt: uint256) -> uint256:
    """
    The vault will re-balance the debt vs target debt. Target debt must be
    smaller or equal to strategy's max_debt. This function will compare the 
    current debt with the target debt and will take funds or deposit new 
    funds to the strategy. 

    The strategy can require a maximum amount of funds that it wants to receive
    to invest. The strategy can also reject freeing funds if they are locked.
    """
    # How much we want the strategy to have.
    new_debt: uint256 = target_debt
    # How much the strategy currently has.
    current_debt: uint256 = self.strategies[strategy].current_debt

    # If the vault is shutdown we can only pull funds.
    if self.shutdown:
        new_debt = 0

    assert new_debt != current_debt, "new debt equals current debt"

    if current_debt > new_debt:
        # Reduce debt.
        assets_to_withdraw: uint256 = unsafe_sub(current_debt, new_debt)

        # Ensure we always have minimum_total_idle when updating debt.
        minimum_total_idle: uint256 = self.minimum_total_idle
        total_idle: uint256 = self.total_idle
        
        # Respect minimum total idle in vault
        if total_idle + assets_to_withdraw < minimum_total_idle:
            assets_to_withdraw = unsafe_sub(minimum_total_idle, total_idle)
            # Cant withdraw more than the strategy has.
            if assets_to_withdraw > current_debt:
                assets_to_withdraw = current_debt

        # Check how much we are able to withdraw.
        # Use maxRedeem and convert since we use redeem.
        withdrawable: uint256 = IStrategy(strategy).convertToAssets(
            IStrategy(strategy).maxRedeem(self)
        )
        assert withdrawable != 0, "nothing to withdraw"

        # If insufficient withdrawable, withdraw what we can.
        if withdrawable < assets_to_withdraw:
            assets_to_withdraw = withdrawable

        # If there are unrealised losses we don't let the vault reduce its debt until there is a new report
        unrealised_losses_share: uint256 = self._assess_share_of_unrealised_losses(strategy, assets_to_withdraw)
        assert unrealised_losses_share == 0, "strategy has unrealised losses"
        
        # Always check the actual amount withdrawn.
        pre_balance: uint256 = ASSET.balanceOf(self)
        self._withdraw_from_strategy(strategy, assets_to_withdraw)
        post_balance: uint256 = ASSET.balanceOf(self)
        
        # making sure we are changing idle according to the real result no matter what. 
        # We pull funds with {redeem} so there can be losses or rounding differences.
        withdrawn: uint256 = min(post_balance - pre_balance, current_debt)

        # If we got too much make sure not to increase PPS.
        if withdrawn > assets_to_withdraw:
            assets_to_withdraw = withdrawn

        # Update storage.
        self.total_idle += withdrawn # actual amount we got.
        # Amount we tried to withdraw in case of losses
        self.total_debt -= assets_to_withdraw 

        new_debt = current_debt - assets_to_withdraw
    else: 
        # We are increasing the strategies debt

        # Revert if target_debt cannot be achieved due to configured max_debt for given strategy
        assert new_debt <= self.strategies[strategy].max_debt, "target debt higher than max debt"

        # Vault is increasing debt with the strategy by sending more funds.
        max_deposit: uint256 = IStrategy(strategy).maxDeposit(self)
        assert max_deposit != 0, "nothing to deposit"

        # Deposit the difference between desired and current.
        assets_to_deposit: uint256 = new_debt - current_debt
        if assets_to_deposit > max_deposit:
            # Deposit as much as possible.
            assets_to_deposit = max_deposit
        
        # Ensure we always have minimum_total_idle when updating debt.
        minimum_total_idle: uint256 = self.minimum_total_idle
        total_idle: uint256 = self.total_idle

        assert total_idle > minimum_total_idle, "no funds to deposit"
        available_idle: uint256 = unsafe_sub(total_idle, minimum_total_idle)

        # If insufficient funds to deposit, transfer only what is free.
        if assets_to_deposit > available_idle:
            assets_to_deposit = available_idle

        # Can't Deposit 0.
        if assets_to_deposit > 0:
            # Approve the strategy to pull only what we are giving it.
            self._erc20_safe_approve(ASSET.address, strategy, assets_to_deposit)

            # Always update based on actual amounts deposited.
            pre_balance: uint256 = ASSET.balanceOf(self)
            IStrategy(strategy).deposit(assets_to_deposit, self)
            post_balance: uint256 = ASSET.balanceOf(self)

            # Make sure our approval is always back to 0.
            self._erc20_safe_approve(ASSET.address, strategy, 0)

            # Making sure we are changing according to the real result no 
            # matter what. This will spend more gas but makes it more robust.
            assets_to_deposit = pre_balance - post_balance

            # Update storage.
            self.total_idle -= assets_to_deposit
            self.total_debt += assets_to_deposit

        new_debt = current_debt + assets_to_deposit

    # Commit memory to storage.
    self.strategies[strategy].current_debt = new_debt

    log DebtUpdated(strategy, current_debt, new_debt)
    return new_debt

## ACCOUNTING MANAGEMENT ##
@internal
def _process_report(strategy: address) -> (uint256, uint256):
    """
    Processing a report means comparing the debt that the strategy has taken 
    with the current amount of funds it is reporting. If the strategy owes 
    less than it currently has, it means it has had a profit, else (assets < debt) 
    it has had a loss.

    Different strategies might choose different reporting strategies: pessimistic, 
    only realised P&L, ... The best way to report depends on the strategy.

    The profit will be distributed following a smooth curve over the vaults 
    profit_max_unlock_time seconds. Losses will be taken immediately, first from the 
    profit buffer (avoiding an impact in pps), then will reduce pps.

    Any applicable fees are charged and distributed during the report as well
    to the specified recipients.
    """
    # Make sure we have a valid strategy.
    assert self.strategies[strategy].activation != 0, "inactive strategy"

    # Burn shares that have been unlocked since the last update
    self._burn_unlocked_shares()

    # Vault assesses profits using 4626 compliant interface. 
    # NOTE: It is important that a strategies `convertToAssets` implementation
    # cannot be manipulated or else the vault could report incorrect gains/losses.
    strategy_shares: uint256 = IStrategy(strategy).balanceOf(self)
    # How much the vaults position is worth.
    total_assets: uint256 = IStrategy(strategy).convertToAssets(strategy_shares)
    # How much the vault had deposited to the strategy.
    current_debt: uint256 = self.strategies[strategy].current_debt

    gain: uint256 = 0
    loss: uint256 = 0

    # Compare reported assets vs. the current debt.
    if total_assets > current_debt:
        # We have a gain.
        gain = unsafe_sub(total_assets, current_debt)
    else:
        # We have a loss.
        loss = unsafe_sub(current_debt, total_assets)

    # For Accountant fee assessment.
    total_fees: uint256 = 0
    total_refunds: uint256 = 0
    # For Protocol fee assessment.
    protocol_fees: uint256 = 0
    protocol_fee_recipient: address = empty(address)

    accountant: address = self.accountant
    # If accountant is not set, fees and refunds remain unchanged.
    if accountant != empty(address):
        total_fees, total_refunds = IAccountant(accountant).report(strategy, gain, loss)

        # Protocol fees will be 0 if accountant fees are 0.
        if total_fees > 0:
            protocol_fee_bps: uint16 = 0
            # Get the config for this vault.
            protocol_fee_bps, protocol_fee_recipient = IFactory(FACTORY).protocol_fee_config()

            if(protocol_fee_bps > 0):
                # Protocol fees are a percent of the fees the accountant is charging.
                protocol_fees = total_fees * convert(protocol_fee_bps, uint256) / MAX_BPS

    # `shares_to_burn` is derived from amounts that would reduce the vaults PPS.
    # NOTE: this needs to be done before any pps changes
    shares_to_burn: uint256 = 0
    accountant_fees_shares: uint256 = 0
    protocol_fees_shares: uint256 = 0
    # Only need to burn shares if there is a loss or fees.
    if loss + total_fees > 0:
        # The amount of shares we will want to burn to offset losses and fees.
        shares_to_burn += self._convert_to_shares(loss + total_fees, Rounding.ROUND_UP)

        # Vault calculates the amount of shares to mint as fees before changing totalAssets / totalSupply.
        if total_fees > 0:
            # Accountant fees are total fees - protocol fees.
            accountant_fees_shares = self._convert_to_shares(total_fees - protocol_fees, Rounding.ROUND_DOWN)
            if protocol_fees > 0:
              protocol_fees_shares = self._convert_to_shares(protocol_fees, Rounding.ROUND_DOWN)

    # Shares to lock is any amounts that would otherwise increase the vaults PPS.
    newly_locked_shares: uint256 = 0
    if total_refunds > 0:
        # Make sure we have enough approval and enough asset to pull.
        total_refunds = min(total_refunds, min(ASSET.balanceOf(accountant), ASSET.allowance(accountant, self)))
        # Transfer the refunded amount of asset to the vault.
        self._erc20_safe_transfer_from(ASSET.address, accountant, self, total_refunds)
        # Update storage to increase total assets.
        self.total_idle += total_refunds

    # Record any reported gains.
    if gain > 0:
        # NOTE: this will increase total_assets
        self.strategies[strategy].current_debt += gain
        self.total_debt += gain

    profit_max_unlock_time: uint256 = self.profit_max_unlock_time
    # Mint anything we are locking to the vault.
    if gain + total_refunds > 0 and profit_max_unlock_time != 0:
        newly_locked_shares = self._issue_shares_for_amount(gain + total_refunds, self)

    # Strategy is reporting a loss
    if loss > 0:
        self.strategies[strategy].current_debt -= loss
        self.total_debt -= loss

    # NOTE: should be precise (no new unlocked shares due to above's burn of shares)
    # newly_locked_shares have already been minted / transferred to the vault, so they need to be subtracted
    # no risk of underflow because they have just been minted.
    previously_locked_shares: uint256 = self.balance_of[self] - newly_locked_shares

    # Now that pps has updated, we can burn the shares we intended to burn as a result of losses/fees.
    # NOTE: If a value reduction (losses / fees) has occurred, prioritize burning locked profit to avoid
    # negative impact on price per share. Price per share is reduced only if losses exceed locked value.
    if shares_to_burn > 0:
        # Cant burn more than the vault owns.
        shares_to_burn = min(shares_to_burn, previously_locked_shares + newly_locked_shares)
        self._burn_shares(shares_to_burn, self)

        # We burn first the newly locked shares, then the previously locked shares.
        shares_not_to_lock: uint256 = min(shares_to_burn, newly_locked_shares)
        # Reduce the amounts to lock by how much we burned
        newly_locked_shares -= shares_not_to_lock
        previously_locked_shares -= (shares_to_burn - shares_not_to_lock)

    # Issue shares for fees that were calculated above if applicable.
    if accountant_fees_shares > 0:
        self._issue_shares(accountant_fees_shares, accountant)

    if protocol_fees_shares > 0:
        self._issue_shares(protocol_fees_shares, protocol_fee_recipient)

    # Update unlocking rate and time to fully unlocked.
    total_locked_shares: uint256 = previously_locked_shares + newly_locked_shares
    if total_locked_shares > 0:
        previously_locked_time: uint256 = 0
        _full_profit_unlock_date: uint256 = self.full_profit_unlock_date
        # Check if we need to account for shares still unlocking.
        if _full_profit_unlock_date > block.timestamp: 
            # There will only be previously locked shares if time remains.
            # We calculate this here since it will not occur every time we lock shares.
            previously_locked_time = previously_locked_shares * (_full_profit_unlock_date - block.timestamp)

        # new_profit_locking_period is a weighted average between the remaining time of the previously locked shares and the profit_max_unlock_time
        new_profit_locking_period: uint256 = (previously_locked_time + newly_locked_shares * profit_max_unlock_time) / total_locked_shares
        # Calculate how many shares unlock per second.
        self.profit_unlocking_rate = total_locked_shares * MAX_BPS_EXTENDED / new_profit_locking_period
        # Calculate how long until the full amount of shares is unlocked.
        self.full_profit_unlock_date = block.timestamp + new_profit_locking_period
        # Update the last profitable report timestamp.
        self.last_profit_update = block.timestamp

    else:
        # NOTE: only setting this to 0 will turn in the desired effect, no need 
        # to update last_profit_update or full_profit_unlock_date
        self.profit_unlocking_rate = 0

    # Record the report of profit timestamp.
    self.strategies[strategy].last_report = block.timestamp

    # We have to recalculate the fees paid for cases with an overall loss.
    log StrategyReported(
        strategy,
        gain,
        loss,
        self.strategies[strategy].current_debt,
        self._convert_to_assets(protocol_fees_shares, Rounding.ROUND_DOWN),
        self._convert_to_assets(protocol_fees_shares + accountant_fees_shares, Rounding.ROUND_DOWN),
        total_refunds
    )

    return (gain, loss)

# SETTERS #
@external
def set_accountant(new_accountant: address):
    """
    @notice Set the new accountant address.
    @param new_accountant The new accountant address.
    """
    self._enforce_role(msg.sender, Roles.ACCOUNTANT_MANAGER)
    self.accountant = new_accountant

    log UpdateAccountant(new_accountant)

@external
def set_default_queue(new_default_queue: DynArray[address, MAX_QUEUE]):
    """
    @notice Set the new default queue array.
    @dev Will check each strategy to make sure it is active.
    @param new_default_queue The new default queue array.
    """
    self._enforce_role(msg.sender, Roles.QUEUE_MANAGER)

    # Make sure every strategy in the new queue is active.
    for strategy in new_default_queue:
        assert self.strategies[strategy].activation != 0, "!inactive"

    # Save the new queue.
    self.default_queue = new_default_queue

    log UpdateDefaultQueue(new_default_queue)

@external
def set_use_default_queue(use_default_queue: bool):
    """
    @notice Set a new value for `use_default_queue`.
    @dev If set `True` the default queue will always be
        used no matter whats passed in.
    @param use_default_queue new value.
    """
    self._enforce_role(msg.sender, Roles.QUEUE_MANAGER)
    self.use_default_queue = use_default_queue

    log UpdateUseDefaultQueue(use_default_queue)

@external
def set_deposit_limit(deposit_limit: uint256):
    """
    @notice Set the new deposit limit.
    @dev Can not be changed if a deposit_limit_module
    is set or if shutdown.
    @param deposit_limit The new deposit limit.
    """
    assert self.shutdown == False # Dev: shutdown
    self._enforce_role(msg.sender, Roles.DEPOSIT_LIMIT_MANAGER)
    assert self.deposit_limit_module == empty(address), "using module"

    self.deposit_limit = deposit_limit

    log UpdateDepositLimit(deposit_limit)

@external
def set_deposit_limit_module(deposit_limit_module: address):
    """
    @notice Set a contract to handle the deposit limit.
    @dev The default `deposit_limit` will need to be set to
    max uint256 since the module will override it.
    @param deposit_limit_module Address of the module.
    """
    assert self.shutdown == False # Dev: shutdown
    self._enforce_role(msg.sender, Roles.DEPOSIT_LIMIT_MANAGER)
    assert self.deposit_limit == max_value(uint256), "using deposit limit"

    self.deposit_limit_module = deposit_limit_module

    log UpdateDepositLimitModule(deposit_limit_module)

@external
def set_withdraw_limit_module(withdraw_limit_module: address):
    """
    @notice Set a contract to handle the withdraw limit.
    @dev This will override the default `max_withdraw`.
    @param withdraw_limit_module Address of the module.
    """
    self._enforce_role(msg.sender, Roles.WITHDRAW_LIMIT_MANAGER)

    self.withdraw_limit_module = withdraw_limit_module

    log UpdateWithdrawLimitModule(withdraw_limit_module)

@external
def set_minimum_total_idle(minimum_total_idle: uint256):
    """
    @notice Set the new minimum total idle.
    @param minimum_total_idle The new minimum total idle.
    """
    self._enforce_role(msg.sender, Roles.MINIMUM_IDLE_MANAGER)
    self.minimum_total_idle = minimum_total_idle

    log UpdateMinimumTotalIdle(minimum_total_idle)

@external
def setProfitMaxUnlockTime(new_profit_max_unlock_time: uint256):
    """
    @notice Set the new profit max unlock time.
    @dev The time is denominated in seconds and must be less than 1 year.
        We only need to update locking period if setting to 0,
        since the current period will use the old rate and on the next
        report it will be reset with the new unlocking time.
    
        Setting to 0 will cause any currently locked profit to instantly
        unlock and an immediate increase in the vaults Price Per Share.

    @param new_profit_max_unlock_time The new profit max unlock time.
    """
    self._enforce_role(msg.sender, Roles.PROFIT_UNLOCK_MANAGER)
    # Must be less than one year for report cycles
    assert new_profit_max_unlock_time <= 31_556_952, "profit unlock time too long"

    # If setting to 0 we need to reset any locked values.
    if (new_profit_max_unlock_time == 0):
        # Burn any shares the vault still has.
        self._burn_shares(self.balance_of[self], self)
        # Reset unlocking variables to 0.
        self.profit_unlocking_rate = 0
        self.full_profit_unlock_date = 0

    self.profit_max_unlock_time = new_profit_max_unlock_time

    log UpdateProfitMaxUnlockTime(new_profit_max_unlock_time)

# ROLE MANAGEMENT #
@internal
def _enforce_role(account: address, role: Roles):
    # Make sure the sender either holds the role or it has been opened.
    assert role in self.roles[account] or self.open_roles[role], "not allowed"

@external
def set_role(account: address, role: Roles):
    """
    @notice Set the roles for an account.
    @dev This will fully override an accounts current roles
     so it should include all roles the account should hold.
    @param account The account to set the role for.
    @param role The roles the account should hold.
    """
    assert msg.sender == self.role_manager
    self.roles[account] = role

    log RoleSet(account, role)

@external
def add_role(account: address, role: Roles):
    """
    @notice Add a new role to an address.
    @dev This will add a new role to the account
     without effecting any of the previously held roles.
    @param account The account to add a role to.
    @param role The new role to add to account.
    """
    assert msg.sender == self.role_manager
    self.roles[account] = self.roles[account] | role

    log RoleSet(account, self.roles[account])

@external
def remove_role(account: address, role: Roles):
    """
    @notice Remove a single role from an account.
    @dev This will leave all other roles for the 
     account unchanged.
    @param account The account to remove a Role from.
    @param role The Role to remove.
    """
    assert msg.sender == self.role_manager
    self.roles[account] = self.roles[account] & ~role

    log RoleSet(account, self.roles[account])

@external
def set_open_role(role: Roles):
    """
    @notice Set a role to be open.
    @param role The role to set.
    """
    assert msg.sender == self.role_manager
    self.open_roles[role] = True

    log RoleStatusChanged(role, RoleStatusChange.OPENED)

@external
def close_open_role(role: Roles):
    """
    @notice Close a opened role.
    @param role The role to close.
    """
    assert msg.sender == self.role_manager
    self.open_roles[role] = False

    log RoleStatusChanged(role, RoleStatusChange.CLOSED)
    
@external
def transfer_role_manager(role_manager: address):
    """
    @notice Step 1 of 2 in order to transfer the 
        role manager to a new address. This will set
        the future_role_manager. Which will then need
        to be accepted by the new manager.
    @param role_manager The new role manager address.
    """
    assert msg.sender == self.role_manager
    self.future_role_manager = role_manager

@external
def accept_role_manager():
    """
    @notice Accept the role manager transfer.
    """
    assert msg.sender == self.future_role_manager
    self.role_manager = msg.sender
    self.future_role_manager = empty(address)

    log UpdateRoleManager(msg.sender)

# VAULT STATUS VIEWS

@view
@external
def isShutdown() -> bool:
    """
    @notice Get if the vault is shutdown.
    @return Bool representing the shutdown status
    """
    return self.shutdown
@view
@external
def unlockedShares() -> uint256:
    """
    @notice Get the amount of shares that have been unlocked.
    @return The amount of shares that are have been unlocked.
    """
    return self._unlocked_shares()

@view
@external
def pricePerShare() -> uint256:
    """
    @notice Get the price per share (pps) of the vault.
    @dev This value offers limited precision. Integrations that require 
        exact precision should use convertToAssets or convertToShares instead.
    @return The price per share.
    """
    return self._convert_to_assets(10 ** DECIMALS, Rounding.ROUND_DOWN)

@view
@external
def get_default_queue() -> DynArray[address, MAX_QUEUE]:
    """
    @notice Get the full default queue currently set.
    @return The current default withdrawal queue.
    """
    return self.default_queue

## REPORTING MANAGEMENT ##
@external
@nonreentrant("lock")
def process_report(strategy: address) -> (uint256, uint256):
    """
    @notice Process the report of a strategy.
    @param strategy The strategy to process the report for.
    @return The gain and loss of the strategy.
    """
    self._enforce_role(msg.sender, Roles.REPORTING_MANAGER)
    return self._process_report(strategy)

@external
@nonreentrant("lock")
def buy_debt(strategy: address, amount: uint256):
    """
    @notice Used for governance to buy bad debt from the vault.
    @dev This should only ever be used in an emergency in place
    of force revoking a strategy in order to not report a loss.
    It allows the DEBT_PURCHASER role to buy the strategies debt
    for an equal amount of `asset`. 

    @param strategy The strategy to buy the debt for
    @param amount The amount of debt to buy from the vault.
    """
    self._enforce_role(msg.sender, Roles.DEBT_PURCHASER)
    assert self.strategies[strategy].activation != 0, "not active"
    
    # Cache the current debt.
    current_debt: uint256 = self.strategies[strategy].current_debt
    _amount: uint256 = amount

    assert current_debt > 0, "nothing to buy"
    assert _amount > 0, "nothing to buy with"
    
    if _amount > current_debt:
        _amount = current_debt

    # We get the proportion of the debt that is being bought and
    # transfer the equivalent shares. We assume this is being used
    # due to strategy issues so won't rely on its conversion rates.
    shares: uint256 = IStrategy(strategy).balanceOf(self) * _amount / current_debt

    assert shares > 0, "cannot buy zero"

    self._erc20_safe_transfer_from(ASSET.address, msg.sender, self, _amount)

    # Lower strategy debt
    self.strategies[strategy].current_debt -= _amount
    # lower total debt
    self.total_debt -= _amount
    # Increase total idle
    self.total_idle += _amount

    # log debt change
    log DebtUpdated(strategy, current_debt, current_debt - _amount)

    # Transfer the strategies shares out.
    self._erc20_safe_transfer(strategy, msg.sender, shares)

    log DebtPurchased(strategy, _amount)

## STRATEGY MANAGEMENT ##
@external
def add_strategy(new_strategy: address):
    """
    @notice Add a new strategy.
    @param new_strategy The new strategy to add.
    """
    self._enforce_role(msg.sender, Roles.ADD_STRATEGY_MANAGER)
    self._add_strategy(new_strategy)

@external
def revoke_strategy(strategy: address):
    """
    @notice Revoke a strategy.
    @param strategy The strategy to revoke.
    """
    self._enforce_role(msg.sender, Roles.REVOKE_STRATEGY_MANAGER)
    self._revoke_strategy(strategy)

@external
def force_revoke_strategy(strategy: address):
    """
    @notice Force revoke a strategy.
    @dev The vault will remove the strategy and write off any debt left 
        in it as a loss. This function is a dangerous function as it can force a 
        strategy to take a loss. All possible assets should be removed from the 
        strategy first via update_debt. If a strategy is removed erroneously it 
        can be re-added and the loss will be credited as profit. Fees will apply.
    @param strategy The strategy to force revoke.
    """
    self._enforce_role(msg.sender, Roles.FORCE_REVOKE_MANAGER)
    self._revoke_strategy(strategy, True)

## DEBT MANAGEMENT ##
@external
def update_max_debt_for_strategy(strategy: address, new_max_debt: uint256):
    """
    @notice Update the max debt for a strategy.
    @param strategy The strategy to update the max debt for.
    @param new_max_debt The new max debt for the strategy.
    """
    self._enforce_role(msg.sender, Roles.MAX_DEBT_MANAGER)
    assert self.strategies[strategy].activation != 0, "inactive strategy"
    self.strategies[strategy].max_debt = new_max_debt

    log UpdatedMaxDebtForStrategy(msg.sender, strategy, new_max_debt)

@external
@nonreentrant("lock")
def update_debt(strategy: address, target_debt: uint256) -> uint256:
    """
    @notice Update the debt for a strategy.
    @param strategy The strategy to update the debt for.
    @param target_debt The target debt for the strategy.
    @return The amount of debt added or removed.
    """
    self._enforce_role(msg.sender, Roles.DEBT_MANAGER)
    return self._update_debt(strategy, target_debt)

## EMERGENCY MANAGEMENT ##
@external
def shutdown_vault():
    """
    @notice Shutdown the vault.
    """
    self._enforce_role(msg.sender, Roles.EMERGENCY_MANAGER)
    assert self.shutdown == False
    
    # Shutdown the vault.
    self.shutdown = True

    # Set deposit limit to 0.
    if self.deposit_limit_module != empty(address):
        self.deposit_limit_module = empty(address)

        log UpdateDepositLimitModule(empty(address))

    self.deposit_limit = 0
    log UpdateDepositLimit(0)

    self.roles[msg.sender] = self.roles[msg.sender] | Roles.DEBT_MANAGER
    log Shutdown()


## SHARE MANAGEMENT ##
## ERC20 + ERC4626 ##
@external
@nonreentrant("lock")
def deposit(assets: uint256, receiver: address) -> uint256:
    """
    @notice Deposit assets into the vault.
    @param assets The amount of assets to deposit.
    @param receiver The address to receive the shares.
    @return The amount of shares minted.
    """
    return self._deposit(msg.sender, receiver, assets)

@external
@nonreentrant("lock")
def mint(shares: uint256, receiver: address) -> uint256:
    """
    @notice Mint shares for the receiver.
    @param shares The amount of shares to mint.
    @param receiver The address to receive the shares.
    @return The amount of assets deposited.
    """
    return self._mint(msg.sender, receiver, shares)

@external
@nonreentrant("lock")
def withdraw(
    assets: uint256, 
    receiver: address, 
    owner: address, 
    max_loss: uint256 = 0,
    strategies: DynArray[address, MAX_QUEUE] = []
) -> uint256:
    """
    @notice Withdraw an amount of asset to `receiver` burning `owner`s shares.
    @dev The default behavior is to not allow any loss.
    @param assets The amount of asset to withdraw.
    @param receiver The address to receive the assets.
    @param owner The address who's shares are being burnt.
    @param max_loss Optional amount of acceptable loss in Basis Points.
    @param strategies Optional array of strategies to withdraw from.
    @return The amount of shares actually burnt.
    """
    shares: uint256 = self._convert_to_shares(assets, Rounding.ROUND_UP)
    self._redeem(msg.sender, receiver, owner, assets, shares, max_loss, strategies)
    return shares

@external
@nonreentrant("lock")
def redeem(
    shares: uint256, 
    receiver: address, 
    owner: address, 
    max_loss: uint256 = MAX_BPS,
    strategies: DynArray[address, MAX_QUEUE] = []
) -> uint256:
    """
    @notice Redeems an amount of shares of `owners` shares sending funds to `receiver`.
    @dev The default behavior is to allow losses to be realized.
    @param shares The amount of shares to burn.
    @param receiver The address to receive the assets.
    @param owner The address who's shares are being burnt.
    @param max_loss Optional amount of acceptable loss in Basis Points.
    @param strategies Optional array of strategies to withdraw from.
    @return The amount of assets actually withdrawn.
    """
    assets: uint256 = self._convert_to_assets(shares, Rounding.ROUND_DOWN)
    # Always return the actual amount of assets withdrawn.
    return self._redeem(msg.sender, receiver, owner, assets, shares, max_loss, strategies)


@external
def approve(spender: address, amount: uint256) -> bool:
    """
    @notice Approve an address to spend the vault's shares.
    @param spender The address to approve.
    @param amount The amount of shares to approve.
    @return True if the approval was successful.
    """
    return self._approve(msg.sender, spender, amount)

@external
def transfer(receiver: address, amount: uint256) -> bool:
    """
    @notice Transfer shares to a receiver.
    @param receiver The address to transfer shares to.
    @param amount The amount of shares to transfer.
    @return True if the transfer was successful.
    """
    assert receiver not in [self, empty(address)]
    self._transfer(msg.sender, receiver, amount)
    return True

@external
def transferFrom(sender: address, receiver: address, amount: uint256) -> bool:
    """
    @notice Transfer shares from a sender to a receiver.
    @param sender The address to transfer shares from.
    @param receiver The address to transfer shares to.
    @param amount The amount of shares to transfer.
    @return True if the transfer was successful.
    """
    assert receiver not in [self, empty(address)]
    return self._transfer_from(sender, receiver, amount)

## ERC20+4626 compatibility
@external
def increaseAllowance(spender: address, amount: uint256) -> bool:
    """
    @notice Increase the allowance for a spender.
    @param spender The address to increase the allowance for.
    @param amount The amount to increase the allowance by.
    @return True if the increase was successful.
    """
    return self._increase_allowance(msg.sender, spender, amount)

@external
def decreaseAllowance(spender: address, amount: uint256) -> bool:
    """
    @notice Decrease the allowance for a spender.
    @param spender The address to decrease the allowance for.
    @param amount The amount to decrease the allowance by.
    @return True if the decrease was successful.
    """
    return self._decrease_allowance(msg.sender, spender, amount)

@external
def permit(
    owner: address, 
    spender: address, 
    amount: uint256, 
    deadline: uint256, 
    v: uint8, 
    r: bytes32, 
    s: bytes32
) -> bool:
    """
    @notice Approve an address to spend the vault's shares.
    @param owner The address to approve.
    @param spender The address to approve.
    @param amount The amount of shares to approve.
    @param deadline The deadline for the permit.
    @param v The v component of the signature.
    @param r The r component of the signature.
    @param s The s component of the signature.
    @return True if the approval was successful.
    """
    return self._permit(owner, spender, amount, deadline, v, r, s)

@view
@external
def balanceOf(addr: address) -> uint256:
    """
    @notice Get the balance of a user.
    @param addr The address to get the balance of.
    @return The balance of the user.
    """
    if(addr == self):
        # If the address is the vault, account for locked shares.
        return self.balance_of[addr] - self._unlocked_shares()

    return self.balance_of[addr]

@view
@external
def totalSupply() -> uint256:
    """
    @notice Get the total supply of shares.
    @return The total supply of shares.
    """
    return self._total_supply()

@view
@external
def asset() -> address:
    """
    @notice Get the address of the asset.
    @return The address of the asset.
    """
    return ASSET.address

@view
@external
def decimals() -> uint8:
    """
    @notice Get the number of decimals of the asset/share.
    @return The number of decimals of the asset/share.
    """
    return convert(DECIMALS, uint8)

@view
@external
def totalAssets() -> uint256:
    """
    @notice Get the total assets held by the vault.
    @return The total assets held by the vault.
    """
    return self._total_assets()

@view
@external
def totalIdle() -> uint256:
    """
    @notice Get the amount of loose `asset` the vault holds.
    @return The current total idle.
    """
    return self.total_idle

@view
@external
def totalDebt() -> uint256:
    """
    @notice Get the the total amount of funds invested
    across all strategies.
    @return The current total debt.
    """
    return self.total_debt

@view
@external
def convertToShares(assets: uint256) -> uint256:
    """
    @notice Convert an amount of assets to shares.
    @param assets The amount of assets to convert.
    @return The amount of shares.
    """
    return self._convert_to_shares(assets, Rounding.ROUND_DOWN)

@view
@external
def previewDeposit(assets: uint256) -> uint256:
    """
    @notice Preview the amount of shares that would be minted for a deposit.
    @param assets The amount of assets to deposit.
    @return The amount of shares that would be minted.
    """
    return self._convert_to_shares(assets, Rounding.ROUND_DOWN)

@view
@external
def previewMint(shares: uint256) -> uint256:
    """
    @notice Preview the amount of assets that would be deposited for a mint.
    @param shares The amount of shares to mint.
    @return The amount of assets that would be deposited.
    """
    return self._convert_to_assets(shares, Rounding.ROUND_UP)

@view
@external
def convertToAssets(shares: uint256) -> uint256:
    """
    @notice Convert an amount of shares to assets.
    @param shares The amount of shares to convert.
    @return The amount of assets.
    """
    return self._convert_to_assets(shares, Rounding.ROUND_DOWN)

@view
@external
def maxDeposit(receiver: address) -> uint256:
    """
    @notice Get the maximum amount of assets that can be deposited.
    @param receiver The address that will receive the shares.
    @return The maximum amount of assets that can be deposited.
    """
    return self._max_deposit(receiver)

@view
@external
def maxMint(receiver: address) -> uint256:
    """
    @notice Get the maximum amount of shares that can be minted.
    @param receiver The address that will receive the shares.
    @return The maximum amount of shares that can be minted.
    """
    max_deposit: uint256 = self._max_deposit(receiver)
    return self._convert_to_shares(max_deposit, Rounding.ROUND_DOWN)

@view
@external
def maxWithdraw(
    owner: address,
    max_loss: uint256 = 0,
    strategies: DynArray[address, MAX_QUEUE] = []
) -> uint256:
    """
    @notice Get the maximum amount of assets that can be withdrawn.
    @dev Complies to normal 4626 interface and takes custom params.
    @param owner The address that owns the shares.
    @param max_loss Custom max_loss if any.
    @param strategies Custom strategies queue if any.
    @return The maximum amount of assets that can be withdrawn.
    """
    return self._max_withdraw(owner, max_loss, strategies)

@view
@external
def maxRedeem(
    owner: address,
    max_loss: uint256 = MAX_BPS,
    strategies: DynArray[address, MAX_QUEUE] = []
) -> uint256:
    """
    @notice Get the maximum amount of shares that can be redeemed.
    @dev Complies to normal 4626 interface and takes custom params.
    @param owner The address that owns the shares.
    @param max_loss Custom max_loss if any.
    @param strategies Custom strategies queue if any.
    @return The maximum amount of shares that can be redeemed.
    """
    return min(
        # Convert to shares is rounding up so we check against the full balance.
        self._convert_to_shares(self._max_withdraw(owner, max_loss, strategies), Rounding.ROUND_UP),
        self.balance_of[owner]
    )

@view
@external
def previewWithdraw(assets: uint256) -> uint256:
    """
    @notice Preview the amount of shares that would be redeemed for a withdraw.
    @param assets The amount of assets to withdraw.
    @return The amount of shares that would be redeemed.
    """
    return self._convert_to_shares(assets, Rounding.ROUND_UP)

@view
@external
def previewRedeem(shares: uint256) -> uint256:
    """
    @notice Preview the amount of assets that would be withdrawn for a redeem.
    @param shares The amount of shares to redeem.
    @return The amount of assets that would be withdrawn.
    """
    return self._convert_to_assets(shares, Rounding.ROUND_DOWN)

@view
@external
def apiVersion() -> String[28]:
    """
    @notice Get the API version of the vault.
    @return The API version of the vault.
    """
    return API_VERSION

@view
@external
def assess_share_of_unrealised_losses(strategy: address, assets_needed: uint256) -> uint256:
    """
    @notice Assess the share of unrealised losses that a strategy has.
    @param strategy The address of the strategy.
    @param assets_needed The amount of assets needed to be withdrawn.
    @return The share of unrealised losses that the strategy has.
    """
    assert self.strategies[strategy].current_debt >= assets_needed

    return self._assess_share_of_unrealised_losses(strategy, assets_needed)

## Profit locking getter functions ##

@view
@external
def profitMaxUnlockTime() -> uint256:
    """
    @notice Gets the current time profits are set to unlock over.
    @return The current profit max unlock time.
    """
    return self.profit_max_unlock_time

@view
@external
def fullProfitUnlockDate() -> uint256:
    """
    @notice Gets the timestamp at which all profits will be unlocked.
    @return The full profit unlocking timestamp
    """
    return self.full_profit_unlock_date

@view
@external
def profitUnlockingRate() -> uint256:
    """
    @notice The per second rate at which profits are unlocking.
    @dev This is denominated in EXTENDED_BPS decimals.
    @return The current profit unlocking rate.
    """
    return self.profit_unlocking_rate


@view
@external
def lastProfitUpdate() -> uint256:
    """
    @notice The timestamp of the last time shares were locked.
    @return The last profit update.
    """
    return self.last_profit_update

# eip-1344
@view
@internal
def domain_separator() -> bytes32:
    return keccak256(
        concat(
            DOMAIN_TYPE_HASH,
            keccak256(convert("Yearn Vault", Bytes[11])),
            keccak256(convert(API_VERSION, Bytes[28])),
            convert(chain.id, bytes32),
            convert(self, bytes32)
        )
    )

@view
@external
def DOMAIN_SEPARATOR() -> bytes32:
    """
    @notice Get the domain separator.
    @return The domain separator.
    """
    return self.domain_separator()

Contract Security Audit

Contract ABI

[{"name":"Deposit","inputs":[{"name":"sender","type":"address","indexed":true},{"name":"owner","type":"address","indexed":true},{"name":"assets","type":"uint256","indexed":false},{"name":"shares","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"Withdraw","inputs":[{"name":"sender","type":"address","indexed":true},{"name":"receiver","type":"address","indexed":true},{"name":"owner","type":"address","indexed":true},{"name":"assets","type":"uint256","indexed":false},{"name":"shares","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"Transfer","inputs":[{"name":"sender","type":"address","indexed":true},{"name":"receiver","type":"address","indexed":true},{"name":"value","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"Approval","inputs":[{"name":"owner","type":"address","indexed":true},{"name":"spender","type":"address","indexed":true},{"name":"value","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"StrategyChanged","inputs":[{"name":"strategy","type":"address","indexed":true},{"name":"change_type","type":"uint256","indexed":true}],"anonymous":false,"type":"event"},{"name":"StrategyReported","inputs":[{"name":"strategy","type":"address","indexed":true},{"name":"gain","type":"uint256","indexed":false},{"name":"loss","type":"uint256","indexed":false},{"name":"current_debt","type":"uint256","indexed":false},{"name":"protocol_fees","type":"uint256","indexed":false},{"name":"total_fees","type":"uint256","indexed":false},{"name":"total_refunds","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"DebtUpdated","inputs":[{"name":"strategy","type":"address","indexed":true},{"name":"current_debt","type":"uint256","indexed":false},{"name":"new_debt","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RoleSet","inputs":[{"name":"account","type":"address","indexed":true},{"name":"role","type":"uint256","indexed":true}],"anonymous":false,"type":"event"},{"name":"RoleStatusChanged","inputs":[{"name":"role","type":"uint256","indexed":true},{"name":"status","type":"uint256","indexed":true}],"anonymous":false,"type":"event"},{"name":"UpdateRoleManager","inputs":[{"name":"role_manager","type":"address","indexed":true}],"anonymous":false,"type":"event"},{"name":"UpdateAccountant","inputs":[{"name":"accountant","type":"address","indexed":true}],"anonymous":false,"type":"event"},{"name":"UpdateDepositLimitModule","inputs":[{"name":"deposit_limit_module","type":"address","indexed":true}],"anonymous":false,"type":"event"},{"name":"UpdateWithdrawLimitModule","inputs":[{"name":"withdraw_limit_module","type":"address","indexed":true}],"anonymous":false,"type":"event"},{"name":"UpdateDefaultQueue","inputs":[{"name":"new_default_queue","type":"address[]","indexed":false}],"anonymous":false,"type":"event"},{"name":"UpdateUseDefaultQueue","inputs":[{"name":"use_default_queue","type":"bool","indexed":false}],"anonymous":false,"type":"event"},{"name":"UpdatedMaxDebtForStrategy","inputs":[{"name":"sender","type":"address","indexed":true},{"name":"strategy","type":"address","indexed":true},{"name":"new_debt","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"UpdateDepositLimit","inputs":[{"name":"deposit_limit","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"UpdateMinimumTotalIdle","inputs":[{"name":"minimum_total_idle","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"UpdateProfitMaxUnlockTime","inputs":[{"name":"profit_max_unlock_time","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"DebtPurchased","inputs":[{"name":"strategy","type":"address","indexed":true},{"name":"amount","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"Shutdown","inputs":[],"anonymous":false,"type":"event"},{"stateMutability":"nonpayable","type":"constructor","inputs":[{"name":"asset","type":"address"},{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"role_manager","type":"address"},{"name":"profit_max_unlock_time","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"set_accountant","inputs":[{"name":"new_accountant","type":"address"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"set_default_queue","inputs":[{"name":"new_default_queue","type":"address[]"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"set_use_default_queue","inputs":[{"name":"use_default_queue","type":"bool"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"set_deposit_limit","inputs":[{"name":"deposit_limit","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"set_deposit_limit_module","inputs":[{"name":"deposit_limit_module","type":"address"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"set_withdraw_limit_module","inputs":[{"name":"withdraw_limit_module","type":"address"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"set_minimum_total_idle","inputs":[{"name":"minimum_total_idle","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"setProfitMaxUnlockTime","inputs":[{"name":"new_profit_max_unlock_time","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"set_role","inputs":[{"name":"account","type":"address"},{"name":"role","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"add_role","inputs":[{"name":"account","type":"address"},{"name":"role","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"remove_role","inputs":[{"name":"account","type":"address"},{"name":"role","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"set_open_role","inputs":[{"name":"role","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"close_open_role","inputs":[{"name":"role","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"transfer_role_manager","inputs":[{"name":"role_manager","type":"address"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"accept_role_manager","inputs":[],"outputs":[]},{"stateMutability":"view","type":"function","name":"isShutdown","inputs":[],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"view","type":"function","name":"unlockedShares","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"pricePerShare","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"get_default_queue","inputs":[],"outputs":[{"name":"","type":"address[]"}]},{"stateMutability":"nonpayable","type":"function","name":"process_report","inputs":[{"name":"strategy","type":"address"}],"outputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"buy_debt","inputs":[{"name":"strategy","type":"address"},{"name":"amount","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"add_strategy","inputs":[{"name":"new_strategy","type":"address"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"revoke_strategy","inputs":[{"name":"strategy","type":"address"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"force_revoke_strategy","inputs":[{"name":"strategy","type":"address"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"update_max_debt_for_strategy","inputs":[{"name":"strategy","type":"address"},{"name":"new_max_debt","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"update_debt","inputs":[{"name":"strategy","type":"address"},{"name":"target_debt","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"shutdown_vault","inputs":[],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"deposit","inputs":[{"name":"assets","type":"uint256"},{"name":"receiver","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"mint","inputs":[{"name":"shares","type":"uint256"},{"name":"receiver","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"withdraw","inputs":[{"name":"assets","type":"uint256"},{"name":"receiver","type":"address"},{"name":"owner","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"withdraw","inputs":[{"name":"assets","type":"uint256"},{"name":"receiver","type":"address"},{"name":"owner","type":"address"},{"name":"max_loss","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"withdraw","inputs":[{"name":"assets","type":"uint256"},{"name":"receiver","type":"address"},{"name":"owner","type":"address"},{"name":"max_loss","type":"uint256"},{"name":"strategies","type":"address[]"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"redeem","inputs":[{"name":"shares","type":"uint256"},{"name":"receiver","type":"address"},{"name":"owner","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"redeem","inputs":[{"name":"shares","type":"uint256"},{"name":"receiver","type":"address"},{"name":"owner","type":"address"},{"name":"max_loss","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"redeem","inputs":[{"name":"shares","type":"uint256"},{"name":"receiver","type":"address"},{"name":"owner","type":"address"},{"name":"max_loss","type":"uint256"},{"name":"strategies","type":"address[]"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"nonpayable","type":"function","name":"approve","inputs":[{"name":"spender","type":"address"},{"name":"amount","type":"uint256"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"nonpayable","type":"function","name":"transfer","inputs":[{"name":"receiver","type":"address"},{"name":"amount","type":"uint256"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"nonpayable","type":"function","name":"transferFrom","inputs":[{"name":"sender","type":"address"},{"name":"receiver","type":"address"},{"name":"amount","type":"uint256"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"nonpayable","type":"function","name":"increaseAllowance","inputs":[{"name":"spender","type":"address"},{"name":"amount","type":"uint256"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"nonpayable","type":"function","name":"decreaseAllowance","inputs":[{"name":"spender","type":"address"},{"name":"amount","type":"uint256"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"nonpayable","type":"function","name":"permit","inputs":[{"name":"owner","type":"address"},{"name":"spender","type":"address"},{"name":"amount","type":"uint256"},{"name":"deadline","type":"uint256"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"view","type":"function","name":"balanceOf","inputs":[{"name":"addr","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"totalSupply","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"asset","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"decimals","inputs":[],"outputs":[{"name":"","type":"uint8"}]},{"stateMutability":"view","type":"function","name":"totalAssets","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"totalIdle","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"totalDebt","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"convertToShares","inputs":[{"name":"assets","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"previewDeposit","inputs":[{"name":"assets","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"previewMint","inputs":[{"name":"shares","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"convertToAssets","inputs":[{"name":"shares","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"maxDeposit","inputs":[{"name":"receiver","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"maxMint","inputs":[{"name":"receiver","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"maxWithdraw","inputs":[{"name":"owner","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"maxWithdraw","inputs":[{"name":"owner","type":"address"},{"name":"max_loss","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"maxWithdraw","inputs":[{"name":"owner","type":"address"},{"name":"max_loss","type":"uint256"},{"name":"strategies","type":"address[]"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"maxRedeem","inputs":[{"name":"owner","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"maxRedeem","inputs":[{"name":"owner","type":"address"},{"name":"max_loss","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"maxRedeem","inputs":[{"name":"owner","type":"address"},{"name":"max_loss","type":"uint256"},{"name":"strategies","type":"address[]"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"previewWithdraw","inputs":[{"name":"assets","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"previewRedeem","inputs":[{"name":"shares","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"apiVersion","inputs":[],"outputs":[{"name":"","type":"string"}]},{"stateMutability":"view","type":"function","name":"assess_share_of_unrealised_losses","inputs":[{"name":"strategy","type":"address"},{"name":"assets_needed","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"profitMaxUnlockTime","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"fullProfitUnlockDate","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"profitUnlockingRate","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"lastProfitUpdate","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"DOMAIN_SEPARATOR","inputs":[],"outputs":[{"name":"","type":"bytes32"}]},{"stateMutability":"view","type":"function","name":"FACTORY","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"strategies","inputs":[{"name":"arg0","type":"address"}],"outputs":[{"name":"","type":"tuple","components":[{"name":"activation","type":"uint256"},{"name":"last_report","type":"uint256"},{"name":"current_debt","type":"uint256"},{"name":"max_debt","type":"uint256"}]}]},{"stateMutability":"view","type":"function","name":"default_queue","inputs":[{"name":"arg0","type":"uint256"}],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"use_default_queue","inputs":[],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"view","type":"function","name":"allowance","inputs":[{"name":"arg0","type":"address"},{"name":"arg1","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"total_supply","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"minimum_total_idle","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"deposit_limit","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"accountant","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"deposit_limit_module","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"withdraw_limit_module","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"roles","inputs":[{"name":"arg0","type":"address"}],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"open_roles","inputs":[{"name":"arg0","type":"uint256"}],"outputs":[{"name":"","type":"bool"}]},{"stateMutability":"view","type":"function","name":"role_manager","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"future_role_manager","inputs":[],"outputs":[{"name":"","type":"address"}]},{"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":"nonces","inputs":[{"name":"arg0","type":"address"}],"outputs":[{"name":"","type":"uint256"}]}]

Deployed Bytecode

0x6003361161000c57612165565b60003560e01c34615a74576371da8a8d811861007a5760243610615a74576004358060a01c615a745760c0523360405260086060526100496159de565b60c05160155560c0517f28709a2dab2a5d5e8688e96159011151c51644ab21839a8a45b449634d7c8b2b600060e0a2005b632d9caa4e81186102585760443610615a7457600435600401600a813511615a745780358060c052600081600a8111615a745780156100da57905b8060051b6020850101358060a01c615a74578160051b60e001526001018181186100b5575b505050503360405260106060526100ef6159de565b600060c051600a8111615a7457801561019757905b8060051b60e001516102205260016102205160205260005260406000205461018c576009610240527f21696e61637469766500000000000000000000000000000000000000000000006102605261024050610240518061026001601f826000031636823750506308c379a061020052602061022052601f19601f61024051011660440161021cfd5b600101818118610104575b505060c051806002558060051b600081601f0160051c600a8111615a745780156101d557905b8060051b60e0015181600301556001018181186101bd575b505050507f0bc0cb8c5ccee13e6a2fd26a699f57ad7ff6e454e6aae97ec41cd2eb9ebd63a5602080610220528061022001600060c0518083528060051b600082600a8111615a7457801561024257905b8060051b60e001518160051b602088010152600101818118610225575b50508201602001915050905081019050610220a1005b6329c8a33b81186102be5760243610615a74576004358060011c615a745760c05233604052601060605261028a6159de565b60c051600d557f1f88e73ebc721f227812938fe07a069ec1f7136aafacb397ed460bd15dee13f160c05160e052602060e0a1005b636fe01d1e81186103835760243610615a7457602154615a7457336040526101006060526102ea6159de565b6016541561034f57600c60c0527f7573696e67206d6f64756c65000000000000000000000000000000000000000060e05260c05060c0518060e001601f826000031636823750506308c379a0608052602060a052601f19601f60c0510116604401609cfd5b6004356014557fae565aab888bca5e19e25a13db7b0c9144305bf55cb0f3f4d724f730e5acdd6260043560c052602060c0a1005b63bb43546681186104565760243610615a74576004358060a01c615a745760c052602154615a7457336040526101006060526103bd6159de565b601454191561042557601360e0527f7573696e67206465706f736974206c696d6974000000000000000000000000006101005260e05060e0518061010001601f826000031636823750506308c379a060a052602060c052601f19601f60e051011660440160bcfd5b60c05160165560c0517f777d215db24fb9fee4ded85c66b422abd7162a1caa6ed3ec4c031f6cd29ada52600060e0a2005b637b67589481186104ba5760243610615a74576004358060a01c615a745760c052336040526102006060526104896159de565b60c05160175560c0517fce6e3f8beda82a13c441d76efd4a6335760f219f38c60502e6680060874e109d600060e0a2005b63bdd81c0181186105135760243610615a7457336040526104006060526104df6159de565b6004356013557f01a4494beed88920b88742cc58f2744e198f55ff192635a1fbabc6be8ffade8160043560c052602060c0a1005b63df69b22a81186106075760243610615a7457336040526108006060526105386159de565b6301e1855860043511156105a357601b60c0527f70726f66697420756e6c6f636b2074696d6520746f6f206c6f6e67000000000060e05260c05060c0518060e001601f826000031636823750506308c379a0608052602060a052601f19601f60c0510116604401609cfd5b6004356105d357600e30602052600052604060002054604052306060526105c861291e565b600060245560006023555b6004356022557ff361aed463da6fa20358e45c6209f1d3e16d4eca706e6eab0b0aeb338729c77a60043560c052602060c0a1005b632cf7fd8581186106815760443610615a74576004358060a01c615a745760405260243580600e1c615a7457606052601a543318615a745760605160186040516020526000526040600020556060516040517f78557646b1d8efa2cd49740d66df5aca39eb610ca8ca0e1ccac08979b6b2c46e60006080a3005b63a97cefa2811861071b5760443610615a74576004358060a01c615a745760405260243580600e1c615a7457606052601a543318615a7457606051601860405160205260005260406000205417601860405160205260005260406000205560186040516020526000526040600020546040517f78557646b1d8efa2cd49740d66df5aca39eb610ca8ca0e1ccac08979b6b2c46e60006080a3005b63e2bf56dd81186107b95760443610615a74576004358060a01c615a745760405260243580600e1c615a7457606052601a543318615a7457606051613fff18601860405160205260005260406000205416601860405160205260005260406000205560186040516020526000526040600020546040517f78557646b1d8efa2cd49740d66df5aca39eb610ca8ca0e1ccac08979b6b2c46e60006080a3005b630b10dd8081186108235760243610615a745760043580600e1c615a7457604052601a543318615a74576001601960405160205260005260406000205560016040517ffe075e51fb76b038a5d44dd2e56b16e6c928e35c0f3cc237312ad09bbca5aee560006060a3005b63d52fe498811861088d5760243610615a745760043580600e1c615a7457604052601a543318615a74576000601960405160205260005260406000205560026040517ffe075e51fb76b038a5d44dd2e56b16e6c928e35c0f3cc237312ad09bbca5aee560006060a3005b63ef54cefd81186108bf5760243610615a74576004358060a01c615a7457604052601a543318615a7457604051601b55005b63f776bf1f811861090d5760043610615a7457601b543318615a745733601a556000601b55337fce93baa0b608a7d420822b6b90cfcccb70574363ba4fd26ef5ac17dd465016c460006040a2005b63bf86d690811861092c5760043610615a745760215460405260206040f35b63d9a0e97a811861094f5760043610615a7457602061094b6080612981565b6080f35b6399530b0681186109955760043610615a745760206020615aa6600039600051604d8111615a745780600a0a905060a052600160c052610990610140612a6e565b610140f35b63a9bbf1cc81186109ff5760043610615a74576020806040528060400160006002548083528060051b600082600a8111615a745780156109eb57905b80600301548160051b6020880101526001018181186109d1575b505082016020019150509050810190506040f35b636ec2b8d48118610a605760243610615a74576004358060a01c615a74576104e052600054600214615a74576002600055336040526020606052610a416159de565b60406104e0516101a052610a566105006151a9565b6105006003600055f35b63e5e918188118610e305760443610615a74576004358060a01c615a74576101a052600054600214615a7457600260005533604052611000606052610aa36159de565b60016101a051602052600052604060002054610b1f57600a6101c0527f6e6f7420616374697665000000000000000000000000000000000000000000006101e0526101c0506101c051806101e001601f826000031636823750506308c379a06101805260206101a052601f19601f6101c051011660440161019cfd5b60016101a0516020526000526040600020600281019050546101c0526024356101e0526101c051610bb057600e610200527f6e6f7468696e6720746f206275790000000000000000000000000000000000006102205261020050610200518061022001601f826000031636823750506308c379a06101c05260206101e052601f19601f6102005101166044016101dcfd5b6101e051610c1e576013610200527f6e6f7468696e6720746f206275792077697468000000000000000000000000006102205261020050610200518061022001601f826000031636823750506308c379a06101c05260206101e052601f19601f6102005101166044016101dcfd5b6101c0516101e0511115610c35576101c0516101e0525b6101a0516370a082316102205230610240526020610220602461023c845afa610c63573d600060003e3d6000fd5b60203d10615a74576102209050516101e051808202811583838304141715615a7457905090506101c0518015615a7457808204905090506102005261020051610d0c57600f610220527f63616e6e6f7420627579207a65726f00000000000000000000000000000000006102405261022050610220518061024001601f826000031636823750506308c379a06101e052602061020052601f19601f6102205101166044016101fcfd5b6020615a8660003960005160405233606052306080526101e05160a052610d31612d0d565b60016101a051602052600052604060002060028101905080546101e051808203828111615a7457905090508155506011546101e051808203828111615a7457905090506011556012546101e051808201828110615a7457905090506012556101a0517f5e2b8821ad6e0e26207e0cb4d242d07eeb1cbb1cfd853e645bdcd27cc5484f956101c051610220526101c0516101e051808203828111615a745790509050610240526040610220a26101a0516040523360605261020051608052610df6612de9565b6101a0517fe94e7f88819f66c19b097748cb754149f63b1a176ed425dee1f1ee933e6d09b06101e051610220526020610220a26003600055005b63de7aeb418118610e745760243610615a74576004358060a01c615a745761010052336040526001606052610e636159de565b61010051604052610e72614573565b005b63577db3168118610ebd5760243610615a74576004358060a01c615a745761022052336040526002606052610ea76159de565b610220516040526000606052610ebb614787565b005b63fd129e638118610f065760243610615a74576004358060a01c615a745761022052336040526004606052610ef06159de565b610220516040526001606052610f04614787565b005b63b9ddcd688118610ff85760443610615a74576004358060a01c615a745760c052336040526080606052610f386159de565b600160c051602052600052604060002054610fac57601160e0527f696e6163746976652073747261746567790000000000000000000000000000006101005260e05060e0518061010001601f826000031636823750506308c379a060a052602060c052601f19601f60e051011660440160bcfd5b602435600160c05160205260005260406000206003810190505560c051337fb3eef2123fec1523a6bbc90aceb203000154c1a4974335fe06b544c7534d4b8960243560e052602060e0a3005b630aeebf5581186110605760443610615a74576004358060a01c615a745761030052600054600214615a7457600260005533604052604060605261103a6159de565b60206103005161016052602435610180526110566103206149de565b6103206003600055f35b6336a5545081186111405760043610615a7457336040526120006060526110856159de565b602154615a74576001602155601654156110c757600060165560007f777d215db24fb9fee4ded85c66b422abd7162a1caa6ed3ec4c031f6cd29ada52600060c0a25b60006014557fae565aab888bca5e19e25a13db7b0c9144305bf55cb0f3f4d724f730e5acdd62600060c052602060c0a16040601833602052600052604060002054176018336020526000526040600020557f4426aa1fb73e391071491fcfe21a88b5c38a0a0333a1f6e77161470439704cf8600060c0a1005b636e553f65811861119c5760443610615a74576024358060a01c615a745761026052600054600214615a745760026000556020336101a052610260516101c0526004356101e05261119261028061369e565b6102806003600055f35b6394bf804d81186111f85760443610615a74576024358060a01c615a745761028052600054600214615a745760026000556020336101a052610280516101c0526004356101e0526111ee6102a061383f565b6102a06003600055f35b63b460af9481186112175760643610615a7457604036610be0376112aa565b63a318c1a4811861123c5760843610615a7457606435610be0526000610c00526112aa565b63d81a09f681186113585760c43610615a7457606435610be052608435600401600a813511615a7457803580610c0052600081600a8111615a745780156112a557905b8060051b6020850101358060a01c615a74578160051b610c20015260010181811861127f575b505050505b6024358060a01c615a7457610ba0526044358060a01c615a7457610bc052600054600214615a7457600260005560043560a052600260c0526112ed610d80612b45565b610d8051610d6052336105e052610ba05161060052610bc0516106205260043561064052610d605161066052610be05161068052610c0051806106a0528060051b806106c082610c2060045afa505050611348610d80613ab7565b610d80506020610d606003600055f35b63ba087652811861137d5760643610615a7457612710610be0526000610c0052611410565b639f40a7b381186113a25760843610615a7457606435610be0526000610c0052611410565b6306580f2d81186114ba5760c43610615a7457606435610be052608435600401600a813511615a7457803580610c0052600081600a8111615a7457801561140b57905b8060051b6020850101358060a01c615a74578160051b610c2001526001018181186113e5575b505050505b6024358060a01c615a7457610ba0526044358060a01c615a7457610bc052600054600214615a7457600260005560043560a052600160c052611453610d80612a6e565b610d8051610d60526020336105e052610ba05161060052610bc05161062052610d60516106405260043561066052610be05161068052610c0051806106a0528060051b806106c082610c2060045afa5050506114b0610d80613ab7565b610d806003600055f35b63095ea7b381186114fb5760443610615a74576004358060a01c615a745760c05260203360405260c0516060526024356080526114f760e061216b565b60e0f35b63a9059cbb81186115605760443610615a74576004358060a01c615a7457610100526101005130811461153057801515611533565b60005b905015615a745733604052610100516060526024356080526115536122b7565b6001610120526020610120f35b6323b872dd81186115d75760643610615a74576004358060a01c615a74576101e0526024358060a01c615a745761020052610200513081146115a4578015156115a7565b60005b905015615a745760206101e05161018052610200516101a0526044356101c0526115d26102206123a6565b610220f35b6339509351811861161a5760443610615a74576004358060a01c615a745760e05260203360405260e0516060526024356080526116156101006123e5565b610100f35b63a457c2d7811861165d5760443610615a74576004358060a01c615a745760e05260203360405260e051606052602435608052611658610100612479565b610100f35b63d505accf81186116d75760e43610615a74576004358060a01c615a7457610460526024358060a01c615a7457610480526084358060081c615a74576104a05260206104605161018052610480516101a052604060446101c0376104a05161020052604060a4610220376116d26104c06125e7565b6104c0f35b6370a0823181186117545760243610615a74576004358060a01c615a7457608052306080511861173957600e60805160205260005260406000205461171c60a0612981565b60a051808203828111615a74579050905060c052602060c0611752565b600e60805160205260005260406000205460a052602060a05bf35b6318160ddd81186117775760043610615a7457602061177360a06129f3565b60a0f35b6338d52e0f811861179e5760043610615a74576020615a8660003960005160405260206040f35b63313ce56781186117cd5760043610615a74576020615aa66000396000518060081c615a745760405260206040f35b6301e1d11481186117f05760043610615a745760206117ec6040612a55565b6040f35b639aa7df94811861180f5760043610615a745760125460405260206040f35b63fc7b9c18811861182e5760043610615a745760115460405260206040f35b63c6e6f592811861185e5760243610615a7457602060043560a052600160c052611859610160612b45565b610160f35b63ef8b30f7811861188e5760243610615a7457602060043560a052600160c052611889610160612b45565b610160f35b63b3d7f6b981186118be5760243610615a7457602060043560a052600260c0526118b9610140612a6e565b610140f35b6307a2d13a81186118ee5760243610615a7457602060043560a052600160c0526118e9610140612a6e565b610140f35b63402d267d81186119255760243610615a74576004358060a01c615a745760c052602060c05160405261192160e061305d565b60e0f35b63c63d75b6811861197f5760243610615a74576004358060a01c615a745761016052610160516040526119596101a061305d565b6101a0516101805260206101805160a052600160c05261197a6101a0612b45565b6101a0f35b63ce96cb77811861199e5760243610615a745760403661060037611a31565b6385b6875681186119c35760443610615a745760243561060052600061062052611a31565b6365cb67658118611a7e5760843610615a745760243561060052604435600401600a813511615a745780358061062052600081600a8111615a74578015611a2c57905b8060051b6020850101358060a01c615a74578160051b6106400152600101818118611a06575b505050505b6004358060a01c615a74576105e05260206105e0516101405261060051610160526106205180610180528060051b806101a08261064060045afa505050611a7961078061323b565b610780f35b63d905777e8118611aa35760243610615a745761271061060052600061062052611b36565b634abe41378118611ac85760443610615a745760243561060052600061062052611b36565b6334b5fab68118611bd15760843610615a745760243561060052604435600401600a813511615a745780358061062052600081600a8111615a74578015611b3157905b8060051b6020850101358060a01c615a74578160051b6106400152600101818118611b0b575b505050505b6004358060a01c615a74576105e0526105e0516101405261060051610160526106205180610180528060051b806101a08261064060045afa505050611b7c61078061323b565b610780516107c05260026107e0526107c05160a0526107e05160c052611ba36107a0612b45565b6107a051600e6105e05160205260005260406000205480828118828410021890509050610800526020610800f35b630a28a4778118611c015760243610615a7457602060043560a052600260c052611bfc610160612b45565b610160f35b634cdad5068118611c315760243610615a7457602060043560a052600160c052611c2c610140612a6e565b610140f35b63258294108118611cb95760043610615a745760208060805260056040527f332e302e3100000000000000000000000000000000000000000000000000000060605260408160800181518082526020830160208301815181525050508051806020830101601f82600003163682375050601f19601f8251602001011690509050810190506080f35b6366d3ae578118611d1a5760443610615a74576004358060a01c615a74576101205260243560016101205160205260005260406000206002810190505410615a7457602061012051604052602435606052611d15610140613108565b610140f35b630952864e8118611d395760043610615a745760225460405260206040f35b632d6326928118611d585760043610615a745760235460405260206040f35b635141eebb8118611d775760043610615a745760245460405260206040f35b638afca8f08118611d965760043610615a745760255460405260206040f35b633644e5158118611dbb5760043610615a74576020611db661018061250d565b610180f35b632dd310008118611de25760043610615a74576020615ac660003960005160405260206040f35b6339ebf8238118611e375760243610615a74576004358060a01c615a74576040526001604051602052600052604060002080546060526001810154608052600281015460a052600381015460c0525060806060f35b638bf03b9e8118611e645760243610615a7457600435600254811015615a74576003015460405260206040f35b631e56558d8118611e835760043610615a7457600d5460405260206040f35b63dd62ed3e8118611edd5760443610615a74576004358060a01c615a74576040526024358060a01c615a7457606052600f604051602052600052604060002080606051602052600052604060002090505460805260206080f35b633940e9ee8118611efc5760043610615a745760105460405260206040f35b63356d64098118611f1b5760043610615a745760135460405260206040f35b63e46a57978118611f3a5760043610615a745760145460405260206040f35b634fb3ccc58118611f595760043610615a745760155460405260206040f35b6361c2ccf48118611f785760043610615a745760165460405260206040f35b63f5ba68f38118611f975760043610615a745760175460405260206040f35b63993746428118611fd25760243610615a74576004358060a01c615a7457604052601860405160205260005260406000205460605260206060f35b63f3789e45811861200d5760243610615a745760043580600e1c615a7457604052601960405160205260005260406000205460605260206060f35b6379b98917811861202c5760043610615a7457601a5460405260206040f35b639a98f418811861204b5760043610615a7457601b5460405260206040f35b6306fdde0381186120d05760043610615a745760208060405280604001601c5480825260208201600082601f0160051c60028111615a745780156120a257905b80601d01548160051b84015260010181811861208b575b505050508051806020830101601f82600003163682375050601f19601f825160200101169050810190506040f35b6395d89b4181186121285760043610615a745760208060405280604001601f5480825260208201602054815250508051806020830101601f82600003163682375050601f19601f825160200101169050810190506040f35b637ecebe0081186121635760243610615a74576004358060a01c615a7457604052602660405160205260005260406000205460605260206060f35b505b60006000fd5b608051600f60405160205260005260406000208060605160205260005260406000209050556060516040517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560805160a052602060a0a36001815250565b600f60c05160205260005260406000208060e0516020526000526040600020905054610120527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61012051146122b5576101005161012051101561228d576016610140527f696e73756666696369656e7420616c6c6f77616e6365000000000000000000006101605261014050610140518061016001601f826000031636823750506308c379a061010052602061012052601f19601f61014051011660440161011cfd5b60c05160405260e0516060526101005161012051036080526122b061014061216b565b610140505b565b600e60405160205260005260406000205460a05260805160a051101561233457601260c0527f696e73756666696369656e742066756e6473000000000000000000000000000060e05260c05060c0518060e001601f826000031636823750506308c379a0608052602060a052601f19601f60c0510116604401609cfd5b60805160a05103600e604051602052600052604060002055608051600e60605160205260005260406000205401600e6060516020526000526040600020556060516040517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60805160c052602060c0a3565b6101805160c0523360e0526101c051610100526123c16121c9565b610180516040526101a0516060526101c0516080526123de6122b7565b6001815250565b600f6040516020526000526040600020806060516020526000526040600020905054608051808201828110615a74579050905060a05260a051600f60405160205260005260406000208060605160205260005260406000209050556060516040517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560a05160c052602060c0a36001815250565b600f6040516020526000526040600020806060516020526000526040600020905054608051808203828111615a74579050905060a05260a051600f60405160205260005260406000208060605160205260005260406000209050556060516040517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560a05160c052602060c0a36001815250565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8160e00152602081019050600b6040527f596561726e205661756c7400000000000000000000000000000000000000000060605260408051602082012090508160e0015260208101905060056080527f332e302e3100000000000000000000000000000000000000000000000000000060a05260808051602082012090508160e00152602081019050468160e00152602081019050308160e001526020810190508060c05260c09050805160208201209050815250565b6101805161265557600d610260527f696e76616c6964206f776e6572000000000000000000000000000000000000006102805261026050610260518061028001601f826000031636823750506308c379a061022052602061024052601f19601f61026051011660440161023cfd5b426101e05110156126c657600e610260527f7065726d697420657870697265640000000000000000000000000000000000006102805261026050610260518061028001601f826000031636823750506308c379a061022052602061024052601f19601f61026051011660440161023cfd5b60266101805160205260005260406000205461026052600060026102a0527f19010000000000000000000000000000000000000000000000000000000000006102c0526102a080516020820183610400018151815250508083019250505061272f6102e061250d565b6102e05181610400015260208101905060007f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9816103200152602081019050610180518161032001526020810190506101a0518161032001526020810190506101c051816103200152602081019050610260518161032001526020810190506101e05181610320015260208101905080610300526103009050805160208201209050816104000152602081019050806103e0526103e090508051602082012090506102805261018051610280516102a052610200516102c052610220516102e05261024051610300526020600060806102a060015afa506000511815612895576011610340527f696e76616c6964207369676e61747572650000000000000000000000000000006103605261034050610340518061036001601f826000031636823750506308c379a061030052602061032052601f19601f61034051011660440161031cfd5b6101c051600f610180516020526000526040600020806101a05160205260005260406000209050556102605160018101818110615a745790506026610180516020526000526040600020556101a051610180517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9256101c0516102a05260206102a0a36001815250565b600e60605160205260005260406000208054604051808203828111615a7457905090508155506040516010540360105560006060517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160805260206080a3565b602354604052600060605242604051116129b457604051156129eb57600e306020526000526040600020546060526129eb565b60245442602554808203828111615a745790509050808202811583838304141715615a74579050905064e8d4a51000810490506060525b606051815250565b601054612a006080612981565b608051808203828111615a745790509050815250565b612a2060c0612981565b60c05160a05260a051612a3257612a53565b426023541115612a4157426025555b60a05160405230606052612a5361291e565b565b601254601154808201828110615a745790509050815250565b60a05119612a7d576001612a82565b60a051155b15612a925760a051815250612b43565b612a9d6101006129f3565b6101005160e05260e051612ab65760a051815250612b43565b60a051612ac4610120612a55565b61012051808202811583838304141715615a745790509050610100526101005160e0518015615a74578082049050905061012052600260c05118612b1d576101005160e0518015615a7457808206905090501515612b20565b60005b15612b3b576101205160018101818110615a74579050610120525b610120518152505b565b60a05119612b54576001612b59565b60a051155b15612b695760a051815250612c39565b612b746101006129f3565b6101005160e052612b86610120612a55565b610120516101005261010051612bb65760e051612bac5760a051815250612c3956612bb6565b6000815250612c39565b60a05160e051808202811583838304141715615a7457905090506101205261012051610100518015615a74578082049050905061014052600260c05118612c135761012051610100518015615a7457808206905090501515612c16565b60005b15612c31576101405160018101818110615a74579050610140525b610140518152505b565b60405163095ea7b360a05260605160c05260805160e052602060a0604460bc6000855af1612c6e573d600060003e3d6000fd5b3d612c8557803b15615a7457600161010052612c9d565b60203d10615a745760a0518060011c615a7457610100525b610100905051612d0b57600f610120527f617070726f76616c206661696c656400000000000000000000000000000000006101405261012050610120518061014001601f826000031636823750506308c379a060e052602061010052601f19601f61012051011660440160fcfd5b565b6040516323b872dd60c05260605160e0526080516101005260a05161012052602060c0606460dc6000855af1612d48573d600060003e3d6000fd5b3d612d5f57803b15615a7457600161014052612d77565b60203d10615a745760c0518060011c615a7457610140525b610140905051612de757600f610160527f7472616e73666572206661696c656400000000000000000000000000000000006101805261016050610160518061018001601f826000031636823750506308c379a061012052602061014052601f19601f61016051011660440161013cfd5b565b60405163a9059cbb60a05260605160c05260805160e052602060a0604460bc6000855af1612e1c573d600060003e3d6000fd5b3d612e3357803b15615a7457600161010052612e4b565b60203d10615a745760a0518060011c615a7457610100525b610100905051612eb957600f610120527f7472616e73666572206661696c656400000000000000000000000000000000006101405261012050610120518061014001601f826000031636823750506308c379a060e052602061010052601f19601f61012051011660440160fcfd5b565b604051600e60605160205260005260406000205401600e606051602052600052604060002055601054604051808201828110615a74579050905060105560605160007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160805260206080a3565b612f366101006129f3565b6101005160e052612f48610120612a55565b610120516101005260006101205260e051612f695760a0516101205261302c565b60a0516101005111612feb5760a051610100511161302c57600f610140527f616d6f756e7420746f6f206869676800000000000000000000000000000000006101605261014050610140518061016001601f826000031636823750506308c379a061010052602061012052601f19601f61014051011660440161011cfd61302c565b60a05160e051808202811583838304141715615a7457905090506101005160a051808203828111615a7457905090508015615a745780820490509050610120525b6101205161303e57600081525061305b565b6101205160405260c051606052613053612ebb565b610120518152505b565b6040518061306c576001613071565b308118155b905015613082576000815250613106565b601654606052606051156130d05760605163320704ed60805260405160a052602060806024609c845afa6130bb573d600060003e3d6000fd5b60203d10615a74576080905051815250613106565b6130da60a0612a55565b60a05160805260145460a05260a051608051106130fb576000815250613106565b60805160a051038152505b565b60016040516020526000526040600020600281019050546080526040516370a0823160c0523060e052602060c0602460dc845afa61314b573d600060003e3d6000fd5b60203d10615a745760c090505160a0526040516307a2d13a60e05260a05161010052602060e0602460fc845afa613187573d600060003e3d6000fd5b60203d10615a745760e090505160c05260805160c05110156131ac57608051156131af565b60015b156131be576000815250613239565b60605160c051808202811583838304141715615a74579050905060e05260605160e0516080518015615a745780820490509050808203828111615a7457905090506101005260e0516080518015615a74578082069050905015613231576101005160018101818110615a74579050610100525b610100518152505b565b600e6101405160205260005260406000205460a052600160c052613260610300612a6e565b610300516102e052601754610300526103005115613333576103005163c36a0eee61032052606061014051610340526101605161036052806103805280610340016000610180518083528060051b600082600a8111615a745780156132df57905b8060051b6101a001518160051b6020880101526001018181186132c1575b5050820160200191505090508101505060206103206101c461033c845afa61330c573d600060003e3d6000fd5b60203d10615a74576103209050516102e0518082811882841002189050905081525061369c565b60125461032052610320516102e051111561369457610320516103405260006103605260025480610380528060051b600081601f0160051c600a8111615a7457801561339457905b80600301548160051b6103a0015260010181811861337b575b5050505061018051156133aa57600d54156133ad565b60005b156133cf576101805180610380528060051b806103a0826101a060045afa5050505b600061038051600a8111615a7457801561368957905b8060051b6103a001516104e05260016104e05160205260005260406000205461346e576011610500527f696e6163746976652073747261746567790000000000000000000000000000006105205261050050610500518061052001601f826000031636823750506308c379a06104c05260206104e052601f19601f6105005101166044016104dcfd5b6102e05161034051808203828111615a74579050905060016104e05160205260005260406000206002810190505480828118828410021890509050610500526104e051604052610500516060526134c6610540613108565b61054051610520526104e0516307a2d13a6105a0526104e05163d905777e6105605230610580526020610560602461057c845afa613509573d600060003e3d6000fd5b60203d10615a74576105609050516105c05260206105a060246105bc845afa613537573d600060003e3d6000fd5b60203d10615a74576105a0905051610540526105005161052051808203828111615a7457905090506105405110156135b5576105205161054051808202811583838304141715615a745790509050610500518015615a745780820490509050610520526105405161052051808201828110615a745790509050610500525b610500516135c25761367e565b61052051156135d95761270f6101605111156135dc565b60005b15613637576103405161050051808201828110615a74579050905061016051808202811583838304141715615a745790509050612710810490506103605161052051808201828110615a745790509050111561363757613689565b6103405161050051808201828110615a745790509050610340526102e051610340511061366357613689565b6103605161052051808201828110615a745790509050610360525b6001018181186133e5575b5050610340516102e0525b6102e0518152505b565b602154615a74576101c0516040526136b761020061305d565b610200516101e051111561372b576014610220527f657863656564206465706f736974206c696d69740000000000000000000000006102405261022050610220518061024001601f826000031636823750506308c379a06101e052602061020052601f19601f6102205101166044016101fcfd5b6020615a8660003960005160405233606052306080526101e05160a052613750612d0d565b6012546101e051808201828110615a7457905090506012556101e05160a0526101c05160c052613781610220612f2b565b6102205161020052610200516137f7576010610220527f63616e6e6f74206d696e74207a65726f000000000000000000000000000000006102405261022050610220518061024001601f826000031636823750506308c379a06101e052602061020052601f19601f6102205101166044016101fcfd5b6101c0516101a0517fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d76101e0516102205261020051610240526040610220a361020051815250565b602154615a74576101e05160a052600260c05261385d610220612a6e565b6102205161020052610200516138d3576013610220527f63616e6e6f74206465706f736974207a65726f000000000000000000000000006102405261022050610220518061024001601f826000031636823750506308c379a06101e052602061020052601f19601f6102205101166044016101fcfd5b6101c0516040526138e561022061305d565b61022051610200511115613959576014610240527f657863656564206465706f736974206c696d69740000000000000000000000006102605261024050610240518061026001601f826000031636823750506308c379a061020052602061022052601f19601f61024051011660440161021cfd5b6020615a8660003960005160405233606052306080526102005160a05261397e612d0d565b60125461020051808201828110615a7457905090506012556101e0516040526101c0516060526139ac612ebb565b6101c0516101a0517fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d761020051610220526101e051610240526040610220a361020051815250565b604051630a28a47760a05260605160c052602060a0602460bc845afa613a1f573d600060003e3d6000fd5b60203d10615a745760a09050516040516370a0823160e0523061010052602060e0602460fc845afa613a56573d600060003e3d6000fd5b60203d10615a745760e09050518082811882841002189050905060805260405163ba08765260a05260805160c0523060e0523061010052602060a0606460bc6000855af1613aa9573d600060003e3d6000fd5b60203d10615a745760a05050565b61060051613b2557600c610800527f5a45524f204144445245535300000000000000000000000000000000000000006108205261080050610800518061082001601f826000031636823750506308c379a06107c05260206107e052601f19601f6108005101166044016107dcfd5b612710610680511115613b98576008610800527f6d6178206c6f73730000000000000000000000000000000000000000000000006108205261080050610800518061082001601f826000031636823750506308c379a06107c05260206107e052601f19601f6108005101166044016107dcfd5b60175415613c4b57610620516101405261068051610160526106a05180610180528060051b806101a0826106c060045afa505050613bd761080061323b565b61080051610640511115613c4b576015610820527f657863656564207769746864726177206c696d697400000000000000000000006108405261082050610820518061084001601f826000031636823750506308c379a06107e052602061080052601f19601f6108205101166044016107fcfd5b6106605161080052600e610620516020526000526040600020546108205261080051613cd7576013610840527f6e6f2073686172657320746f2072656465656d000000000000000000000000006108605261084050610840518061086001601f826000031636823750506308c379a061080052602061082052601f19601f61084051011660440161081cfd5b61080051610820511015613d4b57601d610840527f696e73756666696369656e742073686172657320746f2072656465656d0000006108605261084050610840518061086001601f826000031636823750506308c379a061080052602061082052601f19601f61084051011660440161081cfd5b610620516105e05114613d76576106205160c0526105e05160e0526106605161010052613d766121c9565b6106405161084052601254610860526108605161084051111561440c5760025480610880528060051b600081601f0160051c600a8111615a74578015613dd157905b80600301548160051b6108a00152600101818118613db8575b505050506106a05115613de757600d5415613dea565b60005b15613e0c576106a05180610880528060051b806108a0826106c060045afa5050505b6011546109e052610860516108405103610a00526000610a20526020615a866000396000516370a08231610a605230610a80526020610a606024610a7c845afa613e5b573d600060003e3d6000fd5b60203d10615a7457610a60905051610a4052600061088051600a8111615a7457801561438e57905b8060051b6108a00151610a60526001610a6051602052600052604060002054613f0c576011610a80527f696e616374697665207374726174656779000000000000000000000000000000610aa052610a8050610a805180610aa001601f826000031636823750506308c379a0610a40526020610a6052601f19601f610a80510116604401610a5cfd5b6001610a6051602052600052604060002060028101905054610a8052610a0051610a805180828118828410021890509050610a2052610a60516307a2d13a610b0052610a605163d905777e610ac05230610ae0526020610ac06024610adc845afa613f7c573d600060003e3d6000fd5b60203d10615a7457610ac0905051610b20526020610b006024610b1c845afa613faa573d600060003e3d6000fd5b60203d10615a7457610b00905051610aa052610a6051604052610a2051606052613fd5610ae0613108565b610ae051610ac052610ac0511561416157610a2051610ac051808203828111615a745790509050610aa051101561406c57610a2051610ac051808203828111615a745790509050610ae052610ac051610aa051808202811583838304141715615a745790509050610ae0518015615a745780820490509050610ac052610aa051610ac051808201828110615a745790509050610a20525b610a2051610ac051808203828111615a745790509050610a205261084051610ac051808203828111615a74579050905061084052610a0051610ac051808203828111615a745790509050610a00526109e051610ac051808203828111615a7457905090506109e052610aa0516140e757610ac05115156140ea565b60005b1561416157610a8051610ac051808203828111615a745790509050610ae052610ae0516001610a6051602052600052604060002060028101905055610a60517f5e2b8821ad6e0e26207e0cb4d242d07eeb1cbb1cfd853e645bdcd27cc5484f95610a8051610b0052610ae051610b20526040610b00a25b610a2051610aa05180828118828410021890509050610a2052610a205161418757614383565b610a6051604052610a205160605261419d6139f4565b6020615a866000396000516370a08231610b005230610b20526020610b006024610b1c845afa6141d2573d600060003e3d6000fd5b60203d10615a7457610b00905051610ae052610ae051610a4051808203828111615a745790509050610b00526000610b2052610a2051610b00511161423157610a2051610b0051101561426b57610b0051610a205103610b205261426b565b610a8051610b00511161426257610a2051610a2051610b005103808201828110615a745790509050610a205261426b565b610a8051610a20525b61086051610a2051610b2051808203828111615a745790509050808201828110615a7457905090506108605261084051610b2051808203828111615a745790509050610840526109e051610a2051808203828111615a7457905090506109e052610a8051610a2051610ac051808201828110615a745790509050808203828111615a745790509050610b4052610b40516001610a6051602052600052604060002060028101905055610a60517f5e2b8821ad6e0e26207e0cb4d242d07eeb1cbb1cfd853e645bdcd27cc5484f95610a8051610b6052610b4051610b80526040610b60a26108605161084051116143605761438e565b610ae051610a4052610a0051610a2051808203828111615a745790509050610a00525b600101818118613e83575b50506108405161086051101561440457601c610a60527f696e73756666696369656e742061737365747320696e207661756c7400000000610a8052610a6050610a605180610a8001601f826000031636823750506308c379a0610a20526020610a4052601f19601f610a60510116604401610a3cfd5b6109e0516011555b61084051610640511161442057600061442a565b61270f6106805111155b156144d4576106405161068051808202811583838304141715615a745790509050612710810490506106405161084051808203828111615a74579050905011156144d457600d610880527f746f6f206d756368206c6f7373000000000000000000000000000000000000006108a0526108805061088051806108a001601f826000031636823750506308c379a061084052602061086052601f19601f61088051011660440161085cfd5b61080051604052610620516060526144ea61291e565b6108605161084051808203828111615a7457905090506012556020615a866000396000516040526106005160605261084051608052614527612de9565b61062051610600516105e0517ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db6108405161088052610800516108a0526040610880a461084051815250565b60405130811461458557801515614588565b60005b90506145eb57601f6080527f73747261746567792063616e6e6f74206265207a65726f20616464726573730060a0526080506080518060a001601f826000031636823750506308c379a06040526020606052601f19601f6080510116604401605cfd5b6020615a866000396000516040516338d52e0f606052602060606004607c845afa61461b573d600060003e3d6000fd5b60203d10615a74576060518060a01c615a745760a05260a0905051181561469957600d60c0527f696e76616c69642061737365740000000000000000000000000000000000000060e05260c05060c0518060e001601f826000031636823750506308c379a0608052602060a052601f19601f60c0510116604401609cfd5b60016040516020526000526040600020541561470c5760176060527f737472617465677920616c72656164792061637469766500000000000000000060805260605060605180608001601f826000031636823750506308c379a06020526020604052601f19601f6060510116604401603cfd5b6001604051602052600052604060002042815542600182015560006002820155600060038201555060096002541161475a5760025460098111615a7457600181016002556040518160030155505b60016040517fde8ff765a5c5dad48d27bc9faa99836fb81f3b07c9dc62cfe005475d6b83a2ca60006060a3565b60016040516020526000526040600020546147f95760136080527f7374726174656779206e6f74206163746976650000000000000000000000000060a0526080506080518060a001601f826000031636823750506308c379a06040526020606052601f19601f6080510116604401605cfd5b60006080526001604051602052600052604060002060028101905054156148ea5760605161487e57601160a0527f737472617465677920686173206465627400000000000000000000000000000060c05260a05060a0518060c001601f826000031636823750506308c379a06060526020608052601f19601f60a0510116604401607cfd5b6001604051602052600052604060002060028101905054608052601154608051808203828111615a7457905090506011556040517f7f2ad1d3ba35276f35ef140f83e3e0f17b23064fd710113d3f7a5ab30d267811600060a05260805160c05260803660e03760c060a0a25b600160405160205260005260406000206000815560006001820155600060028201556000600382015550600060a0526000600254600a8111615a7457801561496e57905b80600301546102005260405161020051146149635760a05160098111615a74576001810160a052610200518160051b60c00152505b60010181811861492e575b505060a051806002558060051b600081601f0160051c600a8111615a745780156149ac57905b8060051b60c001518160030155600101818118614994575b5050505060026040517fde8ff765a5c5dad48d27bc9faa99836fb81f3b07c9dc62cfe005475d6b83a2ca6000610200a3565b610180516101a0526001610160516020526000526040600020600281019050546101c05260215415614a115760006101a0525b6101c0516101a05118614a8457601c6101e0527f6e6577206465627420657175616c732063757272656e74206465627400000000610200526101e0506101e0518061020001601f826000031636823750506308c379a06101a05260206101c052601f19601f6101e05101166044016101bcfd5b6101a0516101c05111614e29576001610160516020526000526040600020600381019050546101a0511115614b195760206101e0527f746172676574206465627420686967686572207468616e206d61782064656274610200526101e0506101e0518061020001601f826000031636823750506308c379a06101a05260206101c052601f19601f6101e05101166044016101bcfd5b6101605163402d267d6102005230610220526020610200602461021c845afa614b47573d600060003e3d6000fd5b60203d10615a74576102009050516101e0526101e051614bc7576012610200527f6e6f7468696e6720746f206465706f73697400000000000000000000000000006102205261020050610200518061022001601f826000031636823750506308c379a06101c05260206101e052601f19601f6102005101166044016101dcfd5b6101a0516101c051808203828111615a745790509050610200526101e051610200511115614bf8576101e051610200525b6013546102205260125461024052610220516102405111614c79576013610260527f6e6f2066756e647320746f206465706f736974000000000000000000000000006102805261026050610260518061028001601f826000031636823750506308c379a061022052602061024052601f19601f61026051011660440161023cfd5b6102205161024051036102605261026051610200511115614c9d5761026051610200525b6102005115614e0a576020615a866000396000516040526101605160605261020051608052614cca612c3b565b6020615a866000396000516370a082316102a052306102c05260206102a060246102bc845afa614cff573d600060003e3d6000fd5b60203d10615a74576102a09050516102805261016051636e553f656102a052610200516102c052306102e05260206102a060446102bc6000855af1614d49573d600060003e3d6000fd5b60203d10615a74576102a050506020615a866000396000516370a082316102c052306102e05260206102c060246102dc845afa614d8b573d600060003e3d6000fd5b60203d10615a74576102c09050516102a0526020615a86600039600051604052610160516060526000608052614dbf612c3b565b610280516102a051808203828111615a7457905090506102005260125461020051808203828111615a74579050905060125560115461020051808201828110615a7457905090506011555b6101c05161020051808201828110615a7457905090506101a052615149565b6101a0516101c051036101e052601354610200526012546102205261020051610220516101e051808201828110615a7457905090501015614e88576102205161020051036101e0526101c0516101e0511115614e88576101c0516101e0525b610160516307a2d13a6102a0526101605163d905777e6102605230610280526020610260602461027c845afa614ec3573d600060003e3d6000fd5b60203d10615a74576102609050516102c05260206102a060246102bc845afa614ef1573d600060003e3d6000fd5b60203d10615a74576102a09050516102405261024051614f71576013610260527f6e6f7468696e6720746f207769746864726177000000000000000000000000006102805261026050610260518061028001601f826000031636823750506308c379a061022052602061024052601f19601f61026051011660440161023cfd5b6101e051610240511015614f8857610240516101e0525b610160516040526101e051606052614fa1610280613108565b6102805161026052610260511561501857601e610280527f73747261746567792068617320756e7265616c69736564206c6f7373657300006102a0526102805061028051806102a001601f826000031636823750506308c379a061024052602061026052601f19601f61028051011660440161025cfd5b6020615a866000396000516370a082316102a052306102c05260206102a060246102bc845afa61504d573d600060003e3d6000fd5b60203d10615a74576102a090505161028052610160516040526101e0516060526150756139f4565b6020615a866000396000516370a082316102c052306102e05260206102c060246102dc845afa6150aa573d600060003e3d6000fd5b60203d10615a74576102c09050516102a0526102a05161028051808203828111615a7457905090506101c051808281188284100218905090506102c0526101e0516102c05111156150fe576102c0516101e0525b6012546102c051808201828110615a7457905090506012556011546101e051808203828111615a7457905090506011556101c0516101e051808203828111615a7457905090506101a0525b6101a051600161016051602052600052604060002060028101905055610160517f5e2b8821ad6e0e26207e0cb4d242d07eeb1cbb1cfd853e645bdcd27cc5484f956101c0516101e0526101a0516102005260406101e0a26101a051815250565b60016101a0516020526000526040600020546152255760116101c0527f696e6163746976652073747261746567790000000000000000000000000000006101e0526101c0506101c051806101e001601f826000031636823750506308c379a06101805260206101a052601f19601f6101c051011660440161019cfd5b61522d612a16565b6101a0516370a082316101e052306102005260206101e060246101fc845afa61525b573d600060003e3d6000fd5b60203d10615a74576101e09050516101c0526101a0516307a2d13a610200526101c051610220526020610200602461021c845afa61529e573d600060003e3d6000fd5b60203d10615a74576102009050516101e05260016101a0516020526000526040600020600281019050546102005260403661022037610200516101e051116152f2576101e051610200510361024052615300565b610200516101e05103610220525b608036610260376015546102e0526102e05115615426576102e05163921f8a8f610300526101a05161032052610220516103405261024051610360526040610300606461031c6000855af161535a573d600060003e3d6000fd5b60403d10615a74576103009050805161026052602081015161028052506102605115615426576000610300526020615ac6600039600051635153b199610320526040610320600461033c845afa6153b6573d600060003e3d6000fd5b60403d10615a7457610320518060101c615a745761038052610340518060a01c615a74576103a052610380905080516103005260208101516102c0525060016103005112615426576102605161030051808202811583838304141715615a745790509050612710810490506102a0525b606036610300376102405161026051808201828110615a745790509050156154ee57610300516102405161026051808201828110615a74579050905060a052600260c052615475610360612b45565b61036051808201828110615a7457905090506103005261026051156154ee57610260516102a051808203828111615a74579050905060a052600160c0526154bd610360612b45565b61036051610320526102a051156154ee576102a05160a052600160c0526154e5610360612b45565b61036051610340525b60006103605261028051156155f157610280516020615a866000396000516370a08231610380526102e0516103a0526020610380602461039c845afa615539573d600060003e3d6000fd5b60203d10615a74576103809050516020615a8660003960005163dd62ed3e6103c0526102e0516103e052306104005260206103c060446103dc845afa615584573d600060003e3d6000fd5b60203d10615a74576103c09050518082811882841002189050905080828118828410021890509050610280526020615a866000396000516040526102e051606052306080526102805160a0526155d8612d0d565b60125461028051808201828110615a7457905090506012555b61022051156156415760016101a0516020526000526040600020600281019050805461022051808201828110615a74579050905081555060115461022051808201828110615a7457905090506011555b602254610380526102205161028051808201828110615a7457905090501561566e57610380511515615671565b60005b156156a7576102205161028051808201828110615a74579050905060a0523060c05261569e6103a0612f2b565b6103a051610360525b61024051156156f75760016101a0516020526000526040600020600281019050805461024051808203828111615a74579050905081555060115461024051808203828111615a7457905090506011555b600e3060205260005260406000205461036051808203828111615a7457905090506103a05261030051156157c357610300516103a05161036051808201828110615a7457905090508082811882841002189050905061030052610300516040523060605261576361291e565b6103005161036051808281188284100218905090506103c052610360516103c051808203828111615a745790509050610360526103a051610300516103c051808203828111615a745790509050808203828111615a7457905090506103a0525b61032051156157e257610320516040526102e0516060526157e2612ebb565b610340511561580157610340516040526102c051606052615801612ebb565b6103a05161036051808201828110615a7457905090506103c0526103c051156158fe5760006103e052602354610400524261040051111561586c576103a0516104005142808203828111615a745790509050808202811583838304141715615a7457905090506103e0525b6103e0516103605161038051808202811583838304141715615a745790509050808201828110615a7457905090506103c0518015615a745780820490509050610420526103c05164e8d4a5100081028164e8d4a51000820418615a74579050610420518015615a7457808204905090506024554261042051808201828110615a74579050905060235542602555615904565b60006024555b4260016101a0516020526000526040600020600181019050556101a0517f7f2ad1d3ba35276f35ef140f83e3e0f17b23064fd710113d3f7a5ab30d2678116102205161042052610240516104405260016101a051602052600052604060002060028101905054610460526103405160a052600160c0526159856103e0612a6e565b6103e051610480526103405161032051808201828110615a74579050905060a052600160c0526159b6610400612a6e565b610400516104a052610280516104c05260c0610420a261022051815261024051602082015250565b601860405160205260005260406000205460605116156159ff576001615a11565b60196060516020526000526040600020545b615a7257600b6080527f6e6f7420616c6c6f77656400000000000000000000000000000000000000000060a0526080506080518060a001601f826000031636823750506308c379a06040526020606052601f19601f6080510116604401605cfd5b565b600080fda165767970657283000307000b000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000000012000000000000000000000000e9e8c89c8fc7e8b8f23425688eb68987231178e5

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  ]
[ 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.