ETH Price: $3,842.57 (-4.69%)
Gas: 0.34 Gwei

Contract

0xD9FF8396554A0d18B2CFbeC53e1979b7ecCe8373
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

TokenTracker

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Exchange234597672025-09-28 6:39:1124 days ago1759041551IN
Yield Basis: cyb-wbtc Token
0 ETH0.000022360.12116669
Exchange234342482025-09-24 17:00:3527 days ago1758733235IN
Yield Basis: cyb-wbtc Token
0 ETH0.000121520.73978786
Exchange234342462025-09-24 17:00:1127 days ago1758733211IN
Yield Basis: cyb-wbtc Token
0 ETH0.000124170.67097412
Remove_liquidity234341792025-09-24 16:46:3527 days ago1758732395IN
Yield Basis: cyb-wbtc Token
0 ETH0.000143841.61707662
Add_liquidity234338042025-09-24 15:31:1128 days ago1758727871IN
Yield Basis: cyb-wbtc Token
0 ETH0.000368272.08340858
Add_liquidity234335432025-09-24 14:38:4728 days ago1758724727IN
Yield Basis: cyb-wbtc Token
0 ETH0.000235650.78353044

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Method Block
From
To
0x615c4751234334302025-09-24 14:15:4728 days ago1758723347  Contract Creation0 ETH
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

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

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0xD15fdA1b...567725a39
The constructor portion of the code might be different and could alter the actual behaviour of the contract
This is an ERC-5202 Blueprint contract

Contract Name:
Twocrypto

Compiler Version
vyper:0.4.3

Optimization Enabled:
Yes

Other Settings:
default evmVersion, None license

Contract Source Code (Vyper Json-Input format)

File 1 of 1 : Vyper_contract.vy
# pragma version 0.4.3
# pragma optimize gas
"""
@title Twocrypto
@author Curve.Fi
@license Copyright (c) Curve.Fi, 2025 - all rights reserved
@notice A Curve AMM pool for 2 unpegged assets (e.g. WETH, USD).
@dev All prices in the AMM are with respect to the first token in the pool.
"""

from ethereum.ercs import IERC20
implements: IERC20  # <--------------------- AMM contract is also the LP token.

# --------------------------------- Interfaces -------------------------------

interface Math:
    def wad_exp(_power: int256) -> uint256: view
    def newton_D(
        ANN: uint256,
        gamma: uint256,
        x_unsorted: uint256[N_COINS],
        K0_prev: uint256
    ) -> uint256: view
    def get_y(
        ANN: uint256,
        gamma: uint256,
        x: uint256[N_COINS],
        D: uint256,
        i: uint256,
    ) -> uint256[2]: view
    def get_p(
        _xp: uint256[N_COINS],
        _D: uint256,
        _A_gamma: uint256[2],
    ) -> uint256: view

interface Factory:
    def admin() -> address: view
    def fee_receiver() -> address: view

interface Views:
    def calc_token_amount(
        amounts: uint256[N_COINS], deposit: bool, swap: address
    ) -> uint256: view
    def get_dy(
        i: uint256, j: uint256, dx: uint256, swap: address
    ) -> uint256: view
    def get_dx(
        i: uint256, j: uint256, dy: uint256, swap: address, n_iter: uint256
    ) -> uint256: view


# ------------------------------- Events -------------------------------------

event SetPeriphery:
    views: Views
    math: Math

event Transfer:
    sender: indexed(address)
    receiver: indexed(address)
    value: uint256

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

event TokenExchange:
    buyer: indexed(address)
    sold_id: uint256
    tokens_sold: uint256
    bought_id: uint256
    tokens_bought: uint256
    fee: uint256
    price_scale: uint256

event AddLiquidity:
    provider: indexed(address)
    receiver: indexed(address)
    token_amounts: uint256[N_COINS]
    fee: uint256
    token_supply: uint256
    price_scale: uint256

event Donation:
    donor: indexed(address)
    token_amounts: uint256[N_COINS]

event RemoveLiquidity:
    provider: indexed(address)
    token_amounts: uint256[N_COINS]
    token_supply: uint256

event RemoveLiquidityOne:
    provider: indexed(address)
    token_amount: uint256
    coin_index: uint256
    coin_amount: uint256
    approx_fee: uint256
    packed_price_scale: uint256

event RemoveLiquidityImbalance:
    provider: indexed(address)
    lp_token_amount: uint256
    token_amounts: uint256[N_COINS]
    approx_fee: uint256
    price_scale: uint256

event NewParameters:
    mid_fee: uint256
    out_fee: uint256
    fee_gamma: uint256
    allowed_extra_profit: uint256
    adjustment_step: uint256
    ma_time: uint256

event RampAgamma:
    initial_A: uint256
    future_A: uint256
    initial_gamma: uint256
    future_gamma: uint256
    initial_time: uint256
    future_time: uint256

event StopRampA:
    current_A: uint256
    current_gamma: uint256
    time: uint256

event ClaimAdminFee:
    admin: indexed(address)
    tokens: uint256[N_COINS]

event SetDonationDuration:
    duration: uint256

event SetDonationProtection:
    donation_protection_period: uint256
    donation_protection_lp_threshold: uint256
    donation_shares_max_ratio: uint256

event SetAdminFee:
    admin_fee: uint256

# ----------------------- Storage/State Variables ----------------------------

N_COINS: constant(uint256) = 2
PRECISION: constant(uint256) = 10**18  # <------- The precision to convert to.
PRECISIONS: immutable(uint256[N_COINS])

MATH: public(Math)
VIEW: public(Views)

coins: public(immutable(address[N_COINS]))
factory: public(immutable(Factory))

cached_price_scale: uint256  # <------------------------ Internal price scale.
cached_price_oracle: uint256  # <------- Price target given by moving average.

last_prices: public(uint256)
last_timestamp: public(uint256)

initial_A_gamma: public(uint256)
initial_A_gamma_time: public(uint256)

future_A_gamma: public(uint256)
future_A_gamma_time: public(uint256)  # <------ Time when ramping is finished.
#         This value is 0 (default) when pool is first deployed, and only gets
#        populated by block.timestamp + future_time in `ramp_A_gamma` when the
#                      ramping process is initiated. After ramping is finished
#      (i.e. self.future_A_gamma_time < block.timestamp), the variable is left
#                                                            and not set to 0.

# Donation shares balance
donation_shares: public(uint256)
donation_shares_max_ratio: public(uint256)

# Donations release parameters:
donation_duration: public(uint256)
last_donation_release_ts: public(uint256)

# Donation protection
donation_protection_expiry_ts: public(uint256)
donation_protection_period: public(uint256)
donation_protection_lp_threshold: public(uint256)

balances: public(uint256[N_COINS])
D: public(uint256)
xcp_profit: public(uint256)
xcp_profit_a: public(uint256)  # <--- Full profit at last claim of admin fees.

virtual_price: public(uint256)  # <------ Cached (fast to read) virtual price.
#                          The cached `virtual_price` is also used internally.

# Params that affect how price_scale get adjusted :
packed_rebalancing_params: public(uint256)  # <---------- Contains rebalancing
#               parameters allowed_extra_profit, adjustment_step, and ma_time.

# Fee params that determine dynamic fees:
packed_fee_params: public(uint256)  # <---- Packs mid_fee, out_fee, fee_gamma.

admin_fee: public(uint256)
MAX_ADMIN_FEE: constant(uint256) = 10**10
MIN_FEE: constant(uint256) = 5 * 10**5  # <-------------------------- 0.5 BPS.
MAX_FEE: constant(uint256) = 10 * 10**9
NOISE_FEE: constant(uint256) = 10**5  # <---------------------------- 0.1 BPS.

# ----------------------- Admin params ---------------------------------------

last_admin_fee_claim_timestamp: uint256

MIN_RAMP_TIME: constant(uint256) = 86400
MIN_ADMIN_FEE_CLAIM_INTERVAL: constant(uint256) = 86400

A_MULTIPLIER: constant(uint256) = 10000
# Note on pool internal logic:
# A is scaled by N_COINS in context of StableswapMath.vy
# So A := A_true * N_COINS
MIN_A: constant(uint256) = N_COINS * A_MULTIPLIER # to avoid underflow in math (Ann - multiplier)
MAX_A: constant(uint256) = 10_000 * A_MULTIPLIER # same as in stableswap
MAX_PARAM_CHANGE: constant(uint256) = 10
MIN_GAMMA: constant(uint256) = 10**10
MAX_GAMMA: constant(uint256) = 199 * 10**15 # 1.99 * 10**17

# ----------------------- ERC20 Specific vars --------------------------------

name: public(immutable(String[64]))
symbol: public(immutable(String[32]))
decimals: public(constant(uint8)) = 18
version: public(constant(String[8])) = "v2.1.0d"

balanceOf: public(HashMap[address, uint256])
allowance: public(HashMap[address, HashMap[address, uint256]])
totalSupply: public(uint256)

# ----------------------- Contract -------------------------------------------

@deploy
def __init__(
    _name: String[64],
    _symbol: String[32],
    _coins: address[N_COINS],
    _math: address,
    _salt: bytes32, # not used, left for compatibility with legacy factory
    packed_precisions: uint256,
    packed_gamma_A: uint256,
    packed_fee_params: uint256,
    packed_rebalancing_params: uint256,
    initial_price: uint256,
):

    # these setters must be strreplaced at blueprint deploy time
    # otherwise pool is unusable until set_periphery is called by admin
    self.VIEW = Views(0x35048188c02cbc9239e1e5ecb3761eF9dfDcD31f)
    self.MATH = Math(0x79839c2D74531A8222C0F555865aAc1834e82e51)

    # this parameter can also be dynamically adjusted at blueprint deployment time
    self.admin_fee = 0

    factory = Factory(msg.sender)
    name = _name
    symbol = _symbol
    coins = _coins

    PRECISIONS = self._unpack_2(packed_precisions)  # <-- Precisions of coins.

    # --------------- Validate A and gamma parameters here and not in factory.
    gamma_A: uint256[2] = self._unpack_2(packed_gamma_A)  # gamma is at idx 0.

    assert gamma_A[0] > MIN_GAMMA-1, "gamma<MIN"
    assert gamma_A[0] < MAX_GAMMA+1, "gamma>MAX"

    assert gamma_A[1] > MIN_A-1, "A<MIN"
    assert gamma_A[1] < MAX_A+1, "A>MAX"

    self.initial_A_gamma = packed_gamma_A
    self.future_A_gamma = packed_gamma_A
    # ------------------------------------------------------------------------

    self.packed_rebalancing_params = packed_rebalancing_params  # <-- Contains
    #               rebalancing params: allowed_extra_profit, adjustment_step,
    #                                                         and ma_exp_time.

    self.packed_fee_params = packed_fee_params  # <-------------- Contains Fee
    #                                  params: mid_fee, out_fee and fee_gamma.

    self.cached_price_scale = initial_price
    self.cached_price_oracle = initial_price
    self.last_prices = initial_price
    self.last_timestamp = block.timestamp
    self.xcp_profit_a = 10**18

    self.donation_duration = 7 * 86400


    self.donation_protection_expiry_ts = 0
    self.donation_protection_period = 10 * 60   # 10 minutes
    self.donation_protection_lp_threshold = 20 * PRECISION // 100  # 20%
    self.donation_shares_max_ratio = 10 * PRECISION // 100  # 10%

    log Transfer(sender=empty(address), receiver=self, value=0)  # <------- Fire empty transfer from
    #                                       0x0 to self for indexers to catch.


# ------------------- Token transfers in and out of the AMM ------------------


@internal
def _transfer_in(
    _coin_idx: uint256,
    _dx: uint256,
    sender: address,
    expect_optimistic_transfer: bool,
) -> uint256:
    """
    @notice Transfers `_coin` from `sender` to `self` and calls `callback_sig`
            if it is not empty.
    @params _coin_idx uint256 Index of the coin to transfer in.
    @params dx amount of `_coin` to transfer into the pool.
    @params sender address to transfer `_coin` from.
    @params expect_optimistic_transfer bool True if pool expects user to transfer.
            This is only enabled for exchange_received.
    @return The amount of tokens received.
    """
    coin_balance: uint256 = staticcall IERC20(coins[_coin_idx]).balanceOf(self)

    if expect_optimistic_transfer:  # Only enabled in exchange_received:
        # it expects the caller of exchange_received to have sent tokens to
        # the pool before calling this method.

        # If someone donates extra tokens to the contract: do not acknowledge.
        # We only want to know if there are dx amount of tokens. Anything extra,
        # we ignore. This is why we need to check if received_amounts (which
        # accounts for coin balances of the contract) is atleast dx.
        # If we checked for received_amounts == dx, an extra transfer without a
        # call to exchange_received will break the method.
        dx: uint256 = coin_balance - self.balances[_coin_idx]
        assert dx >= _dx, "!coins"

        # Adjust balances
        self.balances[_coin_idx] += dx

        return dx

    # ----------------------------------------------- ERC20 transferFrom flow.

    # EXTERNAL CALL
    assert extcall IERC20(coins[_coin_idx]).transferFrom(
        sender,
        self,
        _dx,
        default_return_value=True
    ), "!transferFrom"

    dx: uint256 = staticcall IERC20(coins[_coin_idx]).balanceOf(self) - coin_balance
    self.balances[_coin_idx] += dx
    return dx


@internal
def _transfer_out(_coin_idx: uint256, _amount: uint256, receiver: address):
    """
    @notice Transfer a single token from the pool to receiver.
    @params _coin_idx uint256 Index of the token to transfer out
    @params _amount Amount of token to transfer out
    @params receiver Address to send the tokens to
    """

    # Adjust balances before handling transfers:
    self.balances[_coin_idx] -= _amount

    # EXTERNAL CALL
    assert extcall IERC20(coins[_coin_idx]).transfer(
        receiver,
        _amount,
        default_return_value=True
    ), "!transfer"


# -------------------------- AMM Main Functions ------------------------------


@external
@nonreentrant
def exchange(
    i: uint256,
    j: uint256,
    dx: uint256,
    min_dy: uint256,
    receiver: address = msg.sender
) -> uint256:
    """
    @notice Exchange using wrapped native token by default
    @param i Index value for the input coin
    @param j Index value for the output coin
    @param dx Amount of input coin being swapped in
    @param min_dy Minimum amount of output coin to receive
    @param receiver Address to send the output coin to. Default is msg.sender
    @return uint256 Amount of tokens at index j received by the `receiver
    """
    # _transfer_in updates self.balances here:
    dx_received: uint256 = self._transfer_in(
        i,
        dx,
        msg.sender,
        False
    )

    # No ERC20 token transfers occur here:
    out: uint256[3] = self._exchange(
        i,
        j,
        dx_received,
        min_dy,
    )

    # _transfer_out updates self.balances here. Update to state occurs before
    # external calls:
    self._transfer_out(j, out[0], receiver)

    # log:
    log TokenExchange(buyer=msg.sender, sold_id=i, tokens_sold=dx_received, bought_id=j, tokens_bought=out[0], fee=out[1], price_scale=out[2])

    return out[0]


@external
@nonreentrant
def exchange_received(
    i: uint256,
    j: uint256,
    dx: uint256,
    min_dy: uint256,
    receiver: address = msg.sender,
) -> uint256:
    """
    @notice Exchange: but user must transfer dx amount of coin[i] tokens to pool first.
            Pool will not call transferFrom and will only check if a surplus of
            coins[i] is greater than or equal to `dx`.
    @dev Use-case is to reduce the number of redundant ERC20 token
         transfers in zaps. Primarily for dex-aggregators/arbitrageurs/searchers.
         Note for users: please transfer + exchange_received in 1 tx.
    @param i Index value for the input coin
    @param j Index value for the output coin
    @param dx Amount of input coin being swapped in
    @param min_dy Minimum amount of output coin to receive
    @param receiver Address to send the output coin to
    @return uint256 Amount of tokens at index j received by the `receiver`
    """
    # _transfer_in updates self.balances here:
    dx_received: uint256 = self._transfer_in(
        i,
        dx,
        msg.sender,
        True  # <---- expect_optimistic_transfer is set to True here.
    )

    # No ERC20 token transfers occur here:
    out: uint256[3] = self._exchange(
        i,
        j,
        dx_received,
        min_dy,
    )

    # _transfer_out updates self.balances here. Update to state occurs before
    # external calls:
    self._transfer_out(j, out[0], receiver)

    # log:
    log TokenExchange(buyer=msg.sender, sold_id=i, tokens_sold=dx_received, bought_id=j, tokens_bought=out[0], fee=out[1], price_scale=out[2])

    return out[0]


@internal
@view
def _donation_shares(_donation_protection: bool = True) -> uint256:
    """
    @notice Calculates the amount of donation shares that are unlocked and not under protection.
    @dev This function accounts for both time-based release and add_liquidity-based protection.
    """
    donation_shares: uint256 = self.donation_shares
    if donation_shares == 0:
        return 0

    # --- Time-based release of donation shares ---
    elapsed: uint256 = block.timestamp - self.last_donation_release_ts
    unlocked_shares: uint256 = min(donation_shares, donation_shares * elapsed // self.donation_duration)

    if not _donation_protection:
        # if donation protection is disabled, return the total amount of unlocked donation shares
        # this is needed to calculate new timestamp for overlapping donations in add_liquidity
        # otherwise must always be called with donation_protection=True
        return unlocked_shares

    # --- Donation protection damping factor ---
    protection_factor: uint256 = 0
    expiry: uint256 = self.donation_protection_expiry_ts
    if expiry > block.timestamp:
        protection_factor = min((expiry - block.timestamp) * PRECISION // self.donation_protection_period, PRECISION)

    return unlocked_shares * (PRECISION - protection_factor) // PRECISION


@external
@nonreentrant
def add_liquidity(
    amounts: uint256[N_COINS],
    min_mint_amount: uint256,
    receiver: address = msg.sender,
    donation: bool = False
) -> uint256:
    """
    @notice Adds liquidity into the pool.
    @param amounts Amounts of each coin to add.
    @param min_mint_amount Minimum amount of LP to mint.
    @param receiver Address to send the LP tokens to. Default is msg.sender
    @param donation Whether the liquidity is a donation, if True receiver is ignored.
    @return uint256 Amount of LP tokens issued (to receiver or donation buffer).
    """


    assert amounts[0] + amounts[1] > 0, "!amounts"

    # --------------------- Get prices, balances -----------------------------

    old_balances: uint256[N_COINS] = self.balances

    ########################## TRANSFER IN <-------

    amounts_received: uint256[N_COINS] = empty(uint256[N_COINS])
    # This variable will contain the old balances + the amounts received.
    balances: uint256[N_COINS] = self.balances
    for i: uint256 in range(N_COINS):
        if amounts[i] > 0:
            # Updates self.balances here:
            amounts_received[i] = self._transfer_in(
                i,
                amounts[i],
                msg.sender,
                False,  # <--------------------- Disable optimistic transfers.
            )
            balances[i] += amounts_received[i]

    price_scale: uint256 = self.cached_price_scale
    xp: uint256[N_COINS] = self._xp(balances, price_scale)
    old_xp: uint256[N_COINS] = self._xp(old_balances, price_scale)

    # --------------------Finalize ramping of empty pool
    if self.D == 0:
        self.future_A_gamma_time = block.timestamp

    # -------------------- Calculate LP tokens to mint -----------------------

    A_gamma: uint256[2] = self._A_gamma()
    old_D: uint256 = self._get_D(A_gamma, old_xp)

    D: uint256 = staticcall self.MATH.newton_D(A_gamma[0], A_gamma[1], xp, 0)

    token_supply: uint256 = self.totalSupply
    d_token: uint256 = 0
    if old_D > 0:
        d_token = token_supply * D // old_D - token_supply
    else:
        d_token = self._xcp(D, price_scale)  # <----- Making initial virtual price equal to 1.

    assert d_token > 0, "nothing minted"


    d_token_fee: uint256 = 0
    if old_D > 0:
        d_token_fee = (
            self._calc_token_fee(amounts_received, xp, donation, True) * d_token // 10**10 + 1
        ) # for donations - we only take NOISE_FEE (check _calc_token_fee)
        d_token -= d_token_fee

        if donation:
            assert receiver == empty(address), "nonzero receiver"
            new_donation_shares: uint256 = self.donation_shares + d_token
            assert new_donation_shares * PRECISION // (token_supply + d_token) <= self.donation_shares_max_ratio, "donation above cap!"

            # When adding donation, if the previous one hasn't been fully released we preserve
            # the currently unlocked donation [given by `self._donation_shares()`] by updating
            # `self.last_donation_release_ts` as if a single virtual donation of size `new_donation_shares`
            # was made in past and linearly unlocked reaching `self._donation_shares()` at the current time.

            # We want the following equality to hold:
            # self._donation_shares() = new_donation_shares * (new_elapsed / self.donation_duration)
            # We can rearrange this to find the new elapsed time (imitating one large virtual donation):
            # => new_elapsed = self._donation_shares() * self.donation_duration / new_donation_shares
            # edge case: if self.donation_shares = 0, then self._donation_shares() is 0
            # and new_elapsed = 0, thus initializing last_donation_release_ts = block.timestamp
            new_elapsed: uint256 = self._donation_shares(False) * self.donation_duration // new_donation_shares

            # Additional observations:
            # new_elapsed = (old_pool * old_elapsed / D) * D / new_pool = old_elapsed * (old_pool / new_pool)
            # => new_elapsed is always smaller than old_elapsed
            # and self.last_donation_release_ts is carried forward propotionally to new donation size.
            self.last_donation_release_ts = block.timestamp - new_elapsed

            # Credit donation: we don't explicitly mint lp tokens, but increase total supply
            self.donation_shares = new_donation_shares
            self.totalSupply += d_token
            log Donation(donor=msg.sender, token_amounts=amounts_received)
        else:
            # --- Donation Protection & LP Spam Penalty ---
            # Extend protection to shield against donation extraction via sandwich attacks.
            # A penalty is applied for extending the protection to disincentivize spamming.
            relative_lp_add: uint256 = d_token * PRECISION // (token_supply + d_token)
            if relative_lp_add > 0 and self.donation_shares > 0:  # sub-precision additions are expensive to stack
                # Extend protection period
                protection_period: uint256 = self.donation_protection_period
                extension_seconds: uint256 = min(relative_lp_add * protection_period // self.donation_protection_lp_threshold, protection_period)
                current_expiry: uint256 = max(self.donation_protection_expiry_ts, block.timestamp)
                new_expiry: uint256 = min(current_expiry + extension_seconds, block.timestamp + protection_period)
                self.donation_protection_expiry_ts = new_expiry

            # Regular liquidity addition
            self.mint(receiver, d_token)

        price_scale = self.tweak_price(A_gamma, xp, D)

    else:

        # (re)instatiating an empty pool:

        self.D = D
        self.virtual_price = 10**18
        self.xcp_profit = 10**18
        self.xcp_profit_a = 10**18

        self.mint(receiver, d_token)
    assert d_token >= min_mint_amount, "slippage"

    # ---------------------------------------------- Log and claim admin fees.

    log AddLiquidity(
        provider=msg.sender,
        receiver=receiver,
        token_amounts=amounts_received,
        fee=d_token_fee,
        token_supply=token_supply+d_token,
        price_scale=price_scale
    )

    return d_token


@external
@nonreentrant
def remove_liquidity(
    amount: uint256,
    min_amounts: uint256[N_COINS],
    receiver: address = msg.sender,
) -> uint256[N_COINS]:
    """
    @notice This withdrawal method is very safe, does no complex math since
            tokens are withdrawn in balanced proportions. No fees are charged.
    @dev This function intentionally does not rely on any external call to the
            the math contract to make sure that failures in the invariant don't
            prevent users from withdrawing their funds.
    @param amount Amount of LP tokens to burn
    @param min_amounts Minimum amounts of tokens to withdraw
    @param receiver Address to send the withdrawn tokens to
    @return uint256[N_COINS] Amount of pool tokens received by the `receiver`
    """


    # -------------------------------------------------------- Burn LP tokens.

    # We cache the total supply to avoid multiple SLOADs. It is important to do
    # this before the burnFrom call, as the burnFrom call will reduce the supply.
    total_supply: uint256 = self.totalSupply
    self.burnFrom(msg.sender, amount)

    # There are two cases for withdrawing tokens from the pool.
    #   Case 1. Withdrawal does not empty the pool.
    #           In this situation, D is adjusted proportional to the amount of
    #           LP tokens burnt. ERC20 tokens transferred is proportional
    #           to : (AMM balance * LP tokens in) / LP token total supply
    #   Case 2. Withdrawal empties the pool.
    #           In this situation, all tokens are withdrawn and the invariant
    #           is reset.

    withdraw_amounts: uint256[N_COINS] = empty(uint256[N_COINS])
    D: uint256 = self.D # no ramping adjustment to preserve safety of balanced removal

    if amount == total_supply:  # <----------------------------------- Case 2.

        for i: uint256 in range(N_COINS):

            withdraw_amounts[i] = self.balances[i]

    else:  # <-------------------------------------------------------- Case 1.
        for i: uint256 in range(N_COINS):
            # TODO improve comments here
            # Withdraws slightly less -> favors LPs already
            withdraw_amounts[i] = self.balances[i] * amount // total_supply

            assert withdraw_amounts[i] >= min_amounts[i], "slippage"

    # Reduce D proportionally to the amount of tokens leaving. Since withdrawals
    # are balanced, this is a simple subtraction. If amount == total_supply,
    # D will be 0.
    self.D = D - unsafe_div(D * amount, total_supply)

    # ---------------------------------- Transfers ---------------------------

    for i: uint256 in range(N_COINS):
        # _transfer_out updates self.balances here. Update to state occurs
        # before external calls:
        self._transfer_out(i, withdraw_amounts[i], receiver)

    # We intentionally use the unadjusted `amount` here as the amount of lp
    # tokens burnt is `amount`, regardless of the rounding error.
    log RemoveLiquidity(provider=msg.sender, token_amounts=withdraw_amounts, token_supply=total_supply - amount)

    # Take care of leftover donations (only if all LP left)
    self._withdraw_leftover_donations()

    return withdraw_amounts


@external
@nonreentrant
def remove_liquidity_fixed_out(
    token_amount: uint256,
    i: uint256,
    amount_i: uint256,
    min_amount_j: uint256,
    receiver: address = msg.sender
) -> uint256:
    """
    @notice Withdrawal where amount of token i is specified
    @param token_amount LP Token amount to burn
    @param i Index of the coin to withdraw
    @param amount_i exact amount of token i which will be withdrawn
    @param min_amount_j Minimum amount of token j=1-i to withdraw.
    @param receiver Address to send the withdrawn tokens to
    @return Amount of tokens at index j=1-i received by the `receiver`
    """
    return self._remove_liquidity_fixed_out(
        token_amount,
        i,
        amount_i,
        min_amount_j,
        receiver,
    )


@external
@nonreentrant
def remove_liquidity_one_coin(
    lp_token_amount: uint256,
    i: uint256,
    min_amount: uint256,
    receiver: address = msg.sender
) -> uint256:
    """
    @notice Withdraw liquidity in a single coin.
    @param lp_token_amount Amount of LP tokens to burn.
    @param i Index of the coin to withdraw.
    @param min_amount Minimum amount of coin[i] to withdraw.
    @param receiver Address to send the withdrawn tokens to
    @return Amount of coin[i] tokens received by the `receiver`
    """
    return self._remove_liquidity_fixed_out(
        lp_token_amount,
        1 - i, # Here we flip i because we want to constrain the other coin to be zero.
        0, # We set the amount of coin[1 - i] to be withdrawn to 0.
        min_amount,
        receiver,
    )


@internal
def _remove_liquidity_fixed_out(
    token_amount: uint256,
    i: uint256,
    amount_i: uint256,
    min_amount_j: uint256,
    receiver: address,
) -> uint256:

    self._claim_admin_fees()

    A_gamma: uint256[2] = self._A_gamma()

    # Amount of coin[j] withdrawn.
    dy: uint256 = 0
    # New value of D after the withdrawal.
    D: uint256 = 0
    # New scaled balances after the withdrawal.
    xp: uint256[N_COINS] = empty(uint256[N_COINS])
    approx_fee: uint256 = 0

    # ------------------------------------------------------------------------

    dy, D, xp, approx_fee = self._calc_withdraw_fixed_out(
        A_gamma,
        token_amount,
        i,
        amount_i,
    )

    assert dy >= min_amount_j, "slippage"

    # ---------------------------- State Updates -----------------------------

    self.burnFrom(msg.sender, token_amount)

    price_scale: uint256 = self.tweak_price(A_gamma, xp, D)

    if amount_i != 0:
        # one-sided withdrawals call with amount_i = 0, save extcall here
        self._transfer_out(i, amount_i, receiver)

    j: uint256 = 1 - i

    self._transfer_out(j, dy, receiver)

    token_amounts: uint256[N_COINS] = empty(uint256[N_COINS])
    token_amounts[i] = amount_i
    token_amounts[j] = dy

    if amount_i == 0:
        log RemoveLiquidityOne(
            provider=msg.sender,
            token_amount=token_amount,
            coin_index=j,
            coin_amount=dy,
            approx_fee=approx_fee * token_amount // 10**10 + 1, # LP units, not coins!
            packed_price_scale=price_scale
        )
    else:
        log RemoveLiquidityImbalance(
            provider=msg.sender,
            lp_token_amount=token_amount,
            token_amounts=token_amounts,
            approx_fee=approx_fee * token_amount // 10**10 + 1, # LP units
            price_scale=price_scale
        )

    # Take care of leftover donations (only if all LP left)
    self._withdraw_leftover_donations()

    return dy


@internal
def _withdraw_leftover_donations():
    """
    @notice Withdraws leftover donations from the pool.
    This is called when the pool has no other liquidity than donation shares,
    and must be emptied.
    @dev donations go to the factory fees receiver, if not set, to the admin.
    """

    if self.donation_shares != self.totalSupply:
        return

    # Pool has no other LP than donation shares, must be emptied
    receiver: address = staticcall factory.fee_receiver()
    if receiver == empty(address):
        receiver = staticcall factory.admin()

    # empty the pool
    withdraw_amounts: uint256[N_COINS] = self.balances

    for i: uint256 in range(N_COINS):
        # updates self.balances here
        self._transfer_out(i, withdraw_amounts[i], receiver)

    # Update state
    self.donation_shares = 0
    self.totalSupply = 0
    self.D = 0
    self.donation_protection_expiry_ts = 0
    log RemoveLiquidity(provider=receiver, token_amounts=withdraw_amounts, token_supply=0)


# -------------------------- Packing functions -------------------------------


@internal
@pure
def _pack_3(x: uint256[3]) -> uint256:
    """
    @notice Packs 3 integers with values <= 10**18 into a uint256
    @param x The uint256[3] to pack
    @return uint256 Integer with packed values
    """
    return (x[0] << 128) | (x[1] << 64) | x[2]


@internal
@pure
def _unpack_3(_packed: uint256) -> uint256[3]:
    """
    @notice Unpacks a uint256 into 3 integers (values must be <= 10**18)
    @param val The uint256 to unpack
    @return uint256[3] A list of length 3 with unpacked integers
    """
    return [
        (_packed >> 128) & 18446744073709551615,
        (_packed >> 64) & 18446744073709551615,
        _packed & 18446744073709551615,
    ]


@pure
@internal
def _pack_2(p1: uint256, p2: uint256) -> uint256:
    return p1 | (p2 << 128)


@pure
@internal
def _unpack_2(packed: uint256) -> uint256[2]:
    return [packed & (2**128 - 1), packed >> 128]



@internal
def _exchange(
    i: uint256,
    j: uint256,
    dx_received: uint256,
    min_dy: uint256,
) -> uint256[3]:

    assert i != j, "same coin"
    assert dx_received > 0, "zero dx"

    A_gamma: uint256[2] = self._A_gamma()
    balances: uint256[N_COINS] = self.balances
    dy: uint256 = 0

    y: uint256 = balances[j]
    x0: uint256 = balances[i] - dx_received  # old xp[i]

    price_scale: uint256 = self.cached_price_scale
    xp: uint256[N_COINS] = self._xp(balances, price_scale)

    # ----------- Update invariant if A, gamma are undergoing ramps ---------

    if self._is_ramping():

        x0 *= PRECISIONS[i]

        if i > 0:
            x0 = unsafe_div(x0 * price_scale, PRECISION)

        x1: uint256 = xp[i]  # <------------------ Back up old value in xp ...
        xp[i] = x0                                                         # |
        self.D = staticcall self.MATH.newton_D(A_gamma[0], A_gamma[1], xp, 0)   # |
        xp[i] = x1  # <-------------------------------------- ... and restore.

    # ----------------------- Calculate dy and fees --------------------------

    D: uint256 = self.D
    y_out: uint256[2] = staticcall self.MATH.get_y(A_gamma[0], A_gamma[1], xp, D, j)
    dy = xp[j] - y_out[0]
    xp[j] -= dy
    dy -= 1

    if j > 0:
        dy = dy * PRECISION // price_scale
    dy //= PRECISIONS[j]

    fee: uint256 = unsafe_div(self._fee(xp) * dy, 10**10)
    dy -= fee  # <--------------------- Subtract fee from the outgoing amount.
    assert dy >= min_dy, "slippage"
    y -= dy

    y *= PRECISIONS[j]
    if j > 0:
        y = unsafe_div(y * price_scale, PRECISION)
    xp[j] = y  # <------------------------------------------------- Update xp.

    # ------ Tweak price_scale with good initial guess for newton_D ----------

    # Technically a swap wouldn't require to recompute D, however since we're taking
    # fees, we need to update D to reflect the new balances.
    D = staticcall self.MATH.newton_D(A_gamma[0], A_gamma[1], xp, y_out[1])

    price_scale = self.tweak_price(A_gamma, xp, D)

    return [dy, fee, price_scale]


@internal
def tweak_price(
    A_gamma: uint256[2],
    _xp: uint256[N_COINS],
    D: uint256,
) -> uint256:
    """
    @notice Updates price_oracle, last_price and conditionally adjusts
            price_scale. This is called whenever there is an unbalanced
            liquidity operation: _exchange, add_liquidity, or
            remove_liquidity_fixed_out.
    @dev Contains main liquidity rebalancing logic, by tweaking `price_scale`.
    @param A_gamma Array of A and gamma parameters.
    @param _xp Array of current balances.
    @param D New D value.
    @return uint256 The new price_scale.
    """

    # ---------------------------- Read storage ------------------------------

    price_oracle: uint256 = self.cached_price_oracle
    last_prices: uint256 = self.last_prices
    price_scale: uint256 = self.cached_price_scale
    rebalancing_params: uint256[3] = self._unpack_3(self.packed_rebalancing_params)
    is_ramping: bool = self._is_ramping() # store as we bump the timestamp below

    # Contains: allowed_extra_profit, adjustment_step, ma_time. -----^

    # ------------------ Update Price Oracle if needed -----------------------

    last_timestamp: uint256 = self.last_timestamp
    alpha: uint256 = 0
    if last_timestamp < block.timestamp:  # 0th index is for price_oracle.

        #   The moving average price oracle is calculated using the last_price
        #      of the trade at the previous block, and the price oracle logged
        #              before that trade. This can happen only once per block.

        # ------------------ Calculate moving average params -----------------

        alpha = staticcall self.MATH.wad_exp(
            -convert(
                unsafe_div(
                    unsafe_sub(block.timestamp, last_timestamp) * 10**18,
                    rebalancing_params[2]  # <----------------------- ma_time.
                ),
                int256,
            )
        )

        # ---------------------------------------------- Update price oracles.

        # ----------------- We cap state price that goes into the EMA with
        #                                                 2 x price_scale.
        price_oracle = unsafe_div(
            min(last_prices, 2 * price_scale) * (10**18 - alpha) +
            price_oracle * alpha,  # ^-------- Cap spot price into EMA.
            10**18
        )

        self.cached_price_oracle = price_oracle
        self.last_timestamp = block.timestamp

    #  `price_oracle` is used further on to calculate its vector distance from
    # price_scale. This distance is used to calculate the amount of adjustment
    # to be done to the price_scale.
    # ------------------------------------------------------------------------

    # Here we update the spot price, please notice that this value is unsafe
    # and can be manipulated.
    self.last_prices = unsafe_div(
        staticcall self.MATH.get_p(_xp, D, A_gamma) * price_scale,
        10**18
    )

    # ---------- Update profit numbers without price adjustment first --------

    # `totalSupply` might change during this function call.
    total_supply: uint256 = self.totalSupply

    # ===== donation shares (time release + add_liquidity throttling) =====
    donation_shares: uint256 = self._donation_shares()

    # locked_supply contains LP shares and unreleased donations
    locked_supply: uint256 = total_supply - donation_shares

    old_virtual_price: uint256 = self.virtual_price
    xcp: uint256 = self._xcp(D, price_scale)

    virtual_price: uint256 = 10**18 * xcp // total_supply
    # Virtual price can decrease only if A and gamma are being ramped.
    # This does not imply that the virtual price will have increased at the
    # end of this function: it can still decrease if the pool rebalances.
    if virtual_price < old_virtual_price:
        # If A and gamma are being ramped, we allow the virtual price to decrease,
        # as changing the shape of the bonding curve causes losses in the pool.
        assert is_ramping, "virtual price decreased"

    # xcp_profit follows growth of virtual price (and goes down on ramping)
    xcp_profit: uint256 = self.xcp_profit + virtual_price - old_virtual_price
    self.xcp_profit = xcp_profit

    # ------------ Rebalance liquidity if there's enough profits to adjust it:
    #
    # Mathematical basis for rebalancing condition:
    # 1. xcp_profit grows after virtual price, total growth since launch = (xcp_profit − 1)
    # 2. We reserve half of the growth for LPs and admin, rest is used to rebalance the pool

    # Rebalancing condition transformation:
    # virtual_price - 1 > (xcp_profit - 1)/2 + allowed_extra_profit
    # virtual_price > 1 + (xcp_profit - 1)/2 + allowed_extra_profit
    threshold_vp: uint256 = max(10**18, (xcp_profit + 10**18) // 2)

    # The allowed_extra_profit parameter prevents reverting gas-wasting rebalances
    # by ensuring sufficient profit margin

    # user_supply < total_supply => vp_boosted > virtual_price
    # by not accounting for donation shares, virtual_price is boosted leading to rebalance trigger
    # this is approximate condition that preliminary indicates readiness for rebalancing
    vp_boosted: uint256 = 10**18 * xcp // locked_supply
    assert vp_boosted >= virtual_price, "negative donation"
    if (vp_boosted  > threshold_vp + rebalancing_params[0]) and (last_timestamp < block.timestamp):
        #        allowed_extra_profit --------^               #   ^ only rebalance once per block (first tx)
        norm: uint256 = unsafe_div(
            unsafe_mul(price_oracle, 10**18), price_scale
        )
        if norm > 10**18:
            norm = unsafe_sub(norm, 10**18)
        else:
            norm = unsafe_sub(10**18, norm)
        adjustment_step: uint256 = max(
            rebalancing_params[1], unsafe_div(norm, 5)
        )  #           ^------------------------------------- adjustment_step.

        # We only adjust prices if the vector distance between price_oracle
        # and price_scale is large enough. This check ensures that no rebalancing
        # occurs if the distance is low i.e. the pool prices are pegged to the
        # oracle prices.
        if norm > adjustment_step:
            # Calculate new price scale.
            p_new: uint256 = unsafe_div(
                price_scale * unsafe_sub(norm, adjustment_step) +
                adjustment_step * price_oracle,
                norm
            )  # <---- norm is non-zero and gt adjustment_step; unsafe = safe.

            # ---------------- Update stale xp (using price_scale) with p_new.

            xp: uint256[N_COINS] = [
                _xp[0],
                unsafe_div(_xp[1] * p_new, price_scale)
            ]

            # ------------------------------------------ Update D with new xp.
            new_D: uint256 = staticcall self.MATH.newton_D(A_gamma[0], A_gamma[1], xp, 0)
            # --------------------------------------------- Calculate new xcp.
            new_xcp: uint256 = self._xcp(new_D, p_new)
            new_virtual_price: uint256 = 10**18 * new_xcp // total_supply

            donation_shares_to_burn: uint256 = 0
            # burn donations to get to old vp, but not below threshold_vp
            goal_vp: uint256 = max(threshold_vp, virtual_price)
            if new_virtual_price < goal_vp:
                # new_virtual_price is lower than virtual_price.
                # We attempt to boost virtual_price by burning some donation shares
                # This will result in more frequent rebalances.
                #
                #   vp(0)      = xcp /  total_supply          # no burn  -> lowest vp
                #   vp(B)      = xcp / (total_supply – B)     # burn B   -> higher vp
                #
                # Goal: find the *smallest* B such that
                #        vp(B) -> virtual_price (pre-rebalance value)
                #          B   <= donation_shares

                # what would be total supply with (old) virtual_price and new_xcp
                tweaked_supply: uint256 = 10**18 * new_xcp // goal_vp
                assert tweaked_supply < total_supply, "tweaked supply must shrink"
                donation_shares_to_burn = min(
                    unsafe_sub(total_supply, tweaked_supply), # burn the difference between supplies
                    donation_shares # but not more than we can burn (lp shares donation)
                )
                # update virtual price with the tweaked total supply
                new_virtual_price = 10**18 * new_xcp // (total_supply - donation_shares_to_burn)
                # we thus burn some donation shares to compensate for virtual price drop

            if (
                new_virtual_price > 10**18 and
                new_virtual_price >= threshold_vp
                # only rebalance when pool preserves half of the profits
            ):
                self.D = new_D
                self.virtual_price = new_virtual_price
                self.cached_price_scale = p_new

                if donation_shares_to_burn > 0:
                    # Invariant to hold immediately after the burn (measured after protection):
                    #   _donation_shares()' = _donation_shares() - donation_shares_to_burn
                    # We shoud carry forward self.last_donation_release_ts to satisfy the invariant

                    # Get pre-burn state:
                    shares_unlocked: uint256 = self._donation_shares(False)     # time‑unlocked, ignores protection
                    shares_available: uint256 = donation_shares                 # available after protection (computed above self._donation_shares(True))

                    # Invariant: shares_available_new = shares_available - donation_shares_to_burn
                    # Definition: shares_available = shares_unlocked * (1 - protection) [Note: (1 - protection) = shares_available / shares_unlocked]

                    # To reduce shares_available_new by donation_shares_to_burn (B), we should reduce the shares_unlocked_new proportionally:
                    # shares_available_new = shares_available - B = shares_unlocked * (1 - protection) - B = (shares_unlocked - B/(1 - protection)) * (1 - protection)
                    # => shares_unlocked_new = shares_unlocked - B/(1 - protection) = shares_unlocked - B * shares_unlocked / shares_available

                    shares_unlocked_new: uint256 = shares_unlocked - donation_shares_to_burn * shares_unlocked // shares_available

                    #  Definition: shares_unlocked_new = new_total * new_elapsed // donation_duration
                    #  => new_elapsed = shares_unlocked_new * donation_duration // new_total

                    new_total: uint256 = self.donation_shares - donation_shares_to_burn
                    new_elapsed: uint256 = 0
                    if new_total > 0 and shares_unlocked_new > 0:
                        new_elapsed = (shares_unlocked_new * self.donation_duration) // new_total

                    # Apply the burn: update the state and shift the release timestamp
                    self.donation_shares = new_total
                    self.totalSupply -= donation_shares_to_burn
                    self.last_donation_release_ts = block.timestamp - new_elapsed

                return p_new

    # If we end up here price_scale was not adjusted. So we update the state
    # with the virtual price and D we calculated before attempting a rebalance.
    self.D = D
    self.virtual_price = virtual_price

    return price_scale


@internal
def _claim_admin_fees():
    """
    @notice Claims admin fees and sends it to fee_receiver set in the factory.
    @dev Functionally similar to:
         1. Calculating admin's share of fees,
         2. minting LP tokens,
         3. admin claims underlying tokens via remove_liquidity.
    """

    # --------------------- Check if fees can be claimed ---------------------

    # Disable fee claiming if:
    # 1. If time passed since last fee claim is less than
    #    MIN_ADMIN_FEE_CLAIM_INTERVAL.
    # 2. Pool parameters are being ramped.

    last_claim_time: uint256 = self.last_admin_fee_claim_timestamp
    if (
        unsafe_sub(block.timestamp, last_claim_time) < MIN_ADMIN_FEE_CLAIM_INTERVAL or
        self._is_ramping()
    ):
        return

    xcp_profit: uint256 = self.xcp_profit  # <---------- Current pool profits.
    xcp_profit_a: uint256 = self.xcp_profit_a  # <- Profits at previous claim.
    current_lp_token_supply: uint256 = self.totalSupply
    # Do not claim admin fees if:
    # 1. insufficient profits accrued since last claim, and
    # 2. there are less than 10**18 (or 1 unit of) lp tokens, else it can lead
    #    to manipulated virtual prices.

    if xcp_profit <= xcp_profit_a or current_lp_token_supply < 10**18:
        return

    # ---------- Conditions met to claim admin fees: compute state. ----------
    # no _get_D() because we can't claim during ramping
    D: uint256 = self.D

    vprice: uint256 = self.virtual_price
    price_scale: uint256 = self.cached_price_scale
    fee_receiver: address = staticcall factory.fee_receiver()
    balances: uint256[N_COINS] = self.balances

    #  Admin fees are calculated as follows.
    #      1. Calculate accrued profit since last claim. `xcp_profit`
    #         is the current profits. `xcp_profit_a` is the profits
    #         at the previous claim.
    #      2. Take out admin's share, stored in self.admin_fee (with 10**10 precision).
    #      3. Since half of the profits go to rebalancing the pool, we
    #         are left with half; so divide by 2.

    fees: uint256 = unsafe_div(
        unsafe_sub(xcp_profit, xcp_profit_a) * self.admin_fee, 2 * 10**10
    )
    # ------------------------------ Claim admin fees by minting admin's share
    #                                                of the pool in LP tokens.

    admin_share: uint256 = 0
    if fee_receiver != empty(address) and fees > 0:

        # -------------------------------- Calculate admin share to be minted.
        frac: uint256 = vprice * 10**18 // (vprice - fees) - 10**18
        admin_share += current_lp_token_supply * frac // 10**18

        # When claiming fees, the virtual price decreases:
        # Let TS = total_supply, f = fees
        # vp' = xcp/(TS + TS*((vp/vp-f) - 1)) = (xcp/TS) / (1 + f/(vp-f)) =
        # = vp / (vp / (vp-f)) = (vp-f)
        # vp' = (vp-f)

        # Thus, to maintain the condition vp' - 1 > (xcp_profit' - 1)/2:
        #     xcp_profit' := xcp_profit - 2 * f
        xcp_profit -= fees * 2
        # Another way to look at it - we either track admin_claimed_xcp (=sum(fees)),
        # and always use it to calculate admin+LP reserve, or just -=2*fees in xcp_profit.
        # xcp_profit as raw value is thus should't be used in integrations!

    # ------------------- Recalculate virtual_price following admin fee claim.
    total_supply_including_admin_share: uint256 = (
        current_lp_token_supply + admin_share
    )
    vprice = (
        10**18 * self._xcp(D, price_scale) //
        total_supply_including_admin_share
    )

    # Do not claim fees if doing so causes virtual price to drop below 10**18.
    if vprice < 10**18:
        return

    # ---------------------------- Update State ------------------------------

    self.xcp_profit = xcp_profit
    self.last_admin_fee_claim_timestamp = block.timestamp

    # Since we reduce balances: virtual price goes down
    self.virtual_price = vprice

    # Adjust D after admin seemingly removes liquidity
    self.D = D - unsafe_div(D * admin_share, total_supply_including_admin_share)

    if xcp_profit > xcp_profit_a:
        self.xcp_profit_a = xcp_profit  # <-------- Cache last claimed profit.

    # --------------------------- Handle Transfers ---------------------------

    admin_tokens: uint256[N_COINS] = empty(uint256[N_COINS])
    if admin_share > 0:

        for i: uint256 in range(N_COINS):

            admin_tokens[i] = (
                balances[i] * admin_share //
                total_supply_including_admin_share
            )

            # _transfer_out tokens to admin and update self.balances. State
            # update to self.balances occurs before external contract calls:
            self._transfer_out(i, admin_tokens[i], fee_receiver)

        log ClaimAdminFee(admin=fee_receiver, tokens=admin_tokens)


@internal
@view
def _xp(
    balances: uint256[N_COINS],
    price_scale: uint256,
) -> uint256[N_COINS]:
    return [
        balances[0] * PRECISIONS[0],
        unsafe_div(balances[1] * PRECISIONS[1] * price_scale, PRECISION)
    ]

@external
@view
def user_supply() -> uint256:
    """
    @notice Returns the amount of LP tokens that are not locked in donations.
    @return uint256 Amount of LP tokens that are not locked in donations.
    """
    return self.totalSupply - self.donation_shares

@internal
@view
def _is_ramping() -> bool:
    """
    @notice Checks if A and gamma are ramping.
    @return bool True if A and/or gamma are ramping, False otherwise.
    """
    return self.future_A_gamma_time > self.last_timestamp

@internal
@view
def _check_admin():
    assert msg.sender == staticcall factory.admin(), "only owner"

@internal
@view
def _A_gamma() -> uint256[2]:
    t1: uint256 = self.future_A_gamma_time

    A_gamma_1: uint256 = self.future_A_gamma
    gamma1: uint256 = A_gamma_1 & 2**128 - 1
    A1: uint256 = A_gamma_1 >> 128

    if block.timestamp < t1:

        # --------------- Handle ramping up and down of A --------------------

        A_gamma_0: uint256 = self.initial_A_gamma
        t0: uint256 = self.initial_A_gamma_time

        t1 -= t0
        t0 = block.timestamp - t0
        t2: uint256 = t1 - t0

        A1 = ((A_gamma_0 >> 128) * t2 + A1 * t0) // t1
        gamma1 = ((A_gamma_0 & 2**128 - 1) * t2 + gamma1 * t0) // t1

    return [A1, gamma1]


@internal
@view
def _fee(xp: uint256[N_COINS]) -> uint256:

    # unpack mid_fee, out_fee, fee_gamma
    fee_params: uint256[3] = self._unpack_3(self.packed_fee_params)

    # warm up variable with sum of balances
    B: uint256 = xp[0] + xp[1]

    # balance indicator that goes from 10**18 (perfect pool balance) to 0 (very imbalanced, 100:1 and worse)
    # N^N * (xp[0] * xp[1]) / (xp[0] + xp[1])**2
    B = PRECISION * N_COINS**N_COINS * xp[0] // B * xp[1] // B

    # regulate slope using fee_gamma
    # fee_gamma * balance_term / (fee_gamma * balance_term + 1 - balance_term)
    B = fee_params[2] * B // (unsafe_div(fee_params[2] * B, 10**18)  + 10**18 - B)

    # mid_fee * B + out_fee * (1 - B)
    return unsafe_div(fee_params[0] * B + fee_params[1] * (10**18 - B), 10**18)


@internal
@view
def _get_D(A_gamma: uint256[2], xp: uint256[N_COINS]) -> uint256:
    # Normally we need self.D, however, if A and/or gamma are ramping,
    # we need to recalculate D using the current A and gamma values.
    if self._is_ramping():
        # ongoing ramping, recalculate D
        return staticcall self.MATH.newton_D(A_gamma[0], A_gamma[1], xp, 0)
    else:
        # not ramping, use self.D from storage
        return self.D


@internal
@pure
def _xcp(D: uint256, price_scale: uint256) -> uint256:
    # We compute xcp according to the formula in the whitepaper:

    # The following explanation relies on the assumption that the
    # balances have already been scaled by the price scale as shown
    # above.

    # The intuition behind this formula comes from the UniV2
    # whitepaper where the initial amount of LP tokens is set to
    # the geometric mean of the balances, in fact xcp stands for
    # x (balances) constant product.

    # Our invariant behaves in such a way that at the center of the
    # bonding curve:
    # (1) D(x, y) = D(x, x) = 2x.
    # In simple terms this mean that at the center the pool behaves exactly
    # like a constant sum AMM.
    # Here we want to treat the pool as a constant product AMM:
    # (2) xy = k (the constant product invariant).
    # (3) x^2 = k (because we are at the center of the curve where x = y).
    # (4) x = D / 2 (because D(x, y) = 2x in (1]).

    # For xp[0] the price scale is 1 (see whitepaper) so we can obtain
    # x[0] directly from [4]
    # For xp[1] the price scale is != 1 so we divide by the price scale
    # that has unit (coin0/coin1) to convert D (coin0) into xp[1] (coin1):
    # (5) x[1] = D / 2 / price_scale.

    # In the end we take the geometric average of the scaled balances:
    # xcp = sqrt(D // (N_COINS * 1) * D // (N_COINS * price_scale))
    # this is equivalent to D // N_COINS * sqrt(price_scale).
    return D * PRECISION // N_COINS // isqrt(PRECISION * price_scale)


@internal
@view
def _calc_token_fee(amounts: uint256[N_COINS],
                    xp: uint256[N_COINS],
                    donation: bool = False,
                    deposit: bool = False,
                    from_view: bool = False) -> uint256:

    if donation:
        # Donation fees are 0, but NOISE_FEE is required for numerical stability
        return NOISE_FEE

    surplus_amounts: uint256[N_COINS] = amounts
    if from_view:
        # When calling from the view contract no liquidity has been
        # added to the balances.
        surplus_amounts = [0, 0]

    # the ratio of the balances before the liquidity operation
    # balances[0] / balances[1] (adjusted for fixed precisions)
    balances_ratio: uint256 = (self.balances[0] - surplus_amounts[0]) * PRECISIONS[0] * PRECISION // ((self.balances[1] - surplus_amounts[1]) * PRECISIONS[1])
    # We calculate the fee based on the impact on the spot balances.
    # For this reason here (AND ONLY HERE) we use the balances ratio and not
    # the price_scale in self._xp().
    amounts = self._xp(amounts, balances_ratio)

    # fee = sum(amounts_i - avg(amounts)) * fee' / sum(amounts)
    # fee' = _fee(xp) * N_COINS / (4 * (N_COINS - 1)) = _fee(xp)/2 (for N_COINS=2)
    fee: uint256 = unsafe_div(
        unsafe_mul(self._fee(xp), N_COINS),
        unsafe_mul(4, unsafe_sub(N_COINS, 1))
    )

    S: uint256 = 0
    for _x: uint256 in amounts:
        S += _x

    avg: uint256 = unsafe_div(S, N_COINS)
    Sdiff: uint256 = 0

    for _x: uint256 in amounts:
        if _x > avg:
            Sdiff += unsafe_sub(_x, avg)
        else:
            Sdiff += unsafe_sub(avg, _x)

    lp_spam_penalty_fee: uint256 = 0
    if deposit:
        # Penalty fee for spamming add_liquidity into the pool
        current_expiry: uint256 = self.donation_protection_expiry_ts
        if current_expiry > block.timestamp:
            # The penalty is proportional to the remaining protection time and the current pool fee.
            protection_factor: uint256 = min((current_expiry - block.timestamp) * PRECISION // self.donation_protection_period, PRECISION)
            lp_spam_penalty_fee = protection_factor * fee // PRECISION

    return fee * Sdiff // S + NOISE_FEE + lp_spam_penalty_fee

@view
@external
def calc_withdraw_fixed_out(lp_token_amount: uint256, i: uint256, amount_i: uint256) -> uint256:
    """
    @notice Calculate the amounts of coin[1-i] that will be received for burning the lp
    tokens while specifying the amount of coin[i] to be withdrawn.
    @param lp_token_amount LP Token amount to burn.
    @param i index of the token for which the withdrawal amount is specified.
    @param amount_i exact amount of token i which will be withdrawn.
    @return uint256 Amount of token 1-i received for burning token_amount LP tokens.
    """
    return self._calc_withdraw_fixed_out(
        self._A_gamma(),
        lp_token_amount,
        i,
        amount_i,
    )[0]

@view
@external
def calc_withdraw_one_coin(lp_token_amount: uint256, i: uint256) -> uint256:
    """
    @notice Calculate how much of coin[i] will be received when withdrawing liquidity in a single coin.
    @dev This function uses the logic from _calc_withdraw_fixed_out by setting amount_i to 0.
        This forces the withdrawal to be entirely in the other coin.
    @param lp_token_amount LP Token amount to burn.
    @param i index of the token to be withdrawn
    @return uint256 Amount of coin[i] tokens received for burning token_amount LP tokens.
    """
    return self._calc_withdraw_fixed_out(
        self._A_gamma(),
        lp_token_amount,
        1 - i, # Here we flip i because we want to constrain the other coin to be zero.
        0, # We set the amount of coin[1 - i] to be withdrawn to 0.
    )[0]

@internal
@view
def _calc_withdraw_fixed_out(
    A_gamma: uint256[2],
    lp_token_amount: uint256,
    i: uint256,
    amount_i: uint256,
) -> (uint256, uint256, uint256[N_COINS], uint256):
    """
    Withdraws specified number of LP tokens while amount of coin `i` is also specified
    """

    token_supply: uint256 = self.totalSupply
    assert lp_token_amount <= token_supply, "!amount"

    # Since N_COINS = 2, we don't need to check if i < N_COINS
    # because j = 1 - i will underflow for any i > 1
    j: uint256 = 1 - i

    balances: uint256[N_COINS] = self.balances

    # -------------------------- Calculate D0 and xp -------------------------

    price_scale: uint256 = self.cached_price_scale
    xp: uint256[N_COINS] = self._xp(balances, price_scale)
    D: uint256 = self._get_D(A_gamma, xp)

    # We adjust D not to take into account any donated amount. Donations
    # should never be withdrawable by the LPs.

    # ------------------------------ Amounts calc ----------------------------
    dD: uint256 = unsafe_div(lp_token_amount * D, token_supply)
    xp_new: uint256[N_COINS] = xp

    price_scales: uint256[N_COINS] = [PRECISION * PRECISIONS[0], price_scale * PRECISIONS[1]]

    # amountsp (amounts * p) is the dx and dy amounts that the user will receive
    # after the withdrawal scaled for the price scale (p).
    amountsp: uint256[N_COINS] = empty(uint256[N_COINS])
    # This withdrawal method fixes the amount of token i to be withdrawn,
    # this is why here we don't compute amountsp[i] but we give it as a
    # constraint (after appropriate scaling).
    amountsp[i] = unsafe_div(amount_i * price_scales[i], PRECISION)
    xp_new[i] -= amountsp[i]

    # We compute the position on the y axis after a withdrawal of dD with the constraint
    # that xp_new[i] has been reduced by amountsp[i]. This is the new position on the curve
    # after the withdrawal without applying fees.
    y: uint256 = (staticcall self.MATH.get_y(A_gamma[0], A_gamma[1], xp_new, D - dD, j))[0]
    amountsp[j] = xp[j] - y
    xp_new[j] = y

    # _calc_token_fee expects unscaled amounts and without decimals
    # adjustments.
    amounts: uint256[N_COINS] = empty(uint256[N_COINS])
    amounts[i] = amount_i
    if i == 0:
        amounts[1] = amountsp[1] * PRECISION // PRECISIONS[1] // price_scale
    else:
        amounts[0] = amountsp[0] // PRECISIONS[0]

    assert amounts[0] + amounts[1] > 0, "!tokens"
    # The only way to compute the fees is to simulate a withdrawal as we have done
    # above and then rewind and apply the fees.
    approx_fee: uint256 = self._calc_token_fee(amounts, xp_new)
    dD -= dD * approx_fee // 10**10 + 1

    # Same reasoning as before except now we're charging fees.
    y = (staticcall self.MATH.get_y(A_gamma[0], A_gamma[1], xp_new, D - dD, j))[0]
    # We descale y to obtain the amount dy in balances and not scaled balances.
    dy: uint256 = (xp[j] - y) * PRECISION // price_scales[j]
    xp_new[j] = y

    return dy, D - dD, xp_new, approx_fee


# ------------------------ ERC20 functions -----------------------------------


@internal
def _approve(_owner: address, _spender: address, _value: uint256):
    self.allowance[_owner][_spender] = _value

    log Approval(owner=_owner, spender=_spender, value=_value)


@internal
def _transfer(_from: address, _to: address, _value: uint256):
    assert _to not in [self, empty(address)], "!receiver"

    self.balanceOf[_from] -= _value
    self.balanceOf[_to] += _value

    log Transfer(sender=_from, receiver=_to, value=_value)


@external
def transferFrom(_from: address, _to: address, _value: uint256) -> bool:
    """
    @dev Transfer tokens from one address to another.
    @param _from address The address which you want to send tokens from
    @param _to address The address which you want to transfer to
    @param _value uint256 the amount of tokens to be transferred
    @return bool True on successul transfer. Reverts otherwise.
    """
    _allowance: uint256 = self.allowance[_from][msg.sender]
    if _allowance != max_value(uint256):
        self._approve(_from, msg.sender, _allowance - _value)

    self._transfer(_from, _to, _value)
    return True


@external
def transfer(_to: address, _value: uint256) -> bool:
    """
    @dev Transfer token for a specified address
    @param _to The address to transfer to.
    @param _value The amount to be transferred.
    @return bool True on successful transfer. Reverts otherwise.
    """
    self._transfer(msg.sender, _to, _value)
    return True


@external
def approve(_spender: address, _value: uint256) -> bool:
    """
    @notice Allow `_spender` to transfer up to `_value` amount
            of tokens from the caller's account.
    @param _spender The account permitted to spend up to `_value` amount of
                    caller's funds.
    @param _value The amount of tokens `_spender` is allowed to spend.
    @return bool Success
    """
    self._approve(msg.sender, _spender, _value)
    return True


@internal
def mint(_to: address, _value: uint256) -> bool:
    """
    @dev Mint an amount of the token and assigns it to an account.
         This encapsulates the modification of balances such that the
         proper events are emitted.
    @param _to The account that will receive the created tokens.
    @param _value The amount that will be created.
    @return bool Success.
    """
    self.totalSupply += _value
    self.balanceOf[_to] += _value

    log Transfer(sender=empty(address), receiver=_to, value=_value)
    return True


@internal
def burnFrom(_to: address, _value: uint256) -> bool:
    """
    @dev Burn an amount of the token from a given account.
    @param _to The account whose tokens will be burned.
    @param _value The amount that will be burned.
    @return bool Success.
    """
    self.totalSupply -= _value
    self.balanceOf[_to] -= _value

    log Transfer(sender=_to, receiver=empty(address), value=_value)
    return True


# ------------------------- AMM View Functions -------------------------------


@internal
@view
def internal_price_oracle() -> uint256:
    """
    @notice Returns the oracle price of the coin at index `k` w.r.t the coin
            at index 0.
    @dev The oracle is an exponential moving average, with a periodicity
         determined by `self.ma_time`. The aggregated prices are cached state
         prices (dy/dx) calculated AFTER the latest trade.
    @param k The index of the coin.
    @return uint256 Price oracle value of kth coin.
    """
    price_oracle: uint256 = self.cached_price_oracle
    price_scale: uint256 = self.cached_price_scale
    last_prices_timestamp: uint256 = self.last_timestamp

    if last_prices_timestamp < block.timestamp:  # <------------ Update moving
        #                                                   average if needed.

        last_prices: uint256 = self.last_prices
        ma_time: uint256 = self._unpack_3(self.packed_rebalancing_params)[2]
        alpha: uint256 = staticcall self.MATH.wad_exp(
            -convert(
                unsafe_sub(block.timestamp, last_prices_timestamp) * 10**18 // ma_time,
                int256,
            )
        )

        # ---- We cap state price that goes into the EMA with 2 x price_scale.
        return (
            min(last_prices, 2 * price_scale) * (10**18 - alpha) +
            price_oracle * alpha
        ) // 10**18

    return price_oracle


@external
@view
def fee_receiver() -> address:
    """
    @notice Returns the address of the admin fee receiver.
    @return address Fee receiver.
    """
    return staticcall factory.fee_receiver()


@external
@view
def admin() -> address:
    """
    @notice Returns the address of the pool's admin.
    @return address Admin.
    """
    return staticcall factory.admin()


@external
@view
def calc_token_amount(amounts: uint256[N_COINS], deposit: bool) -> uint256:
    """
    @notice Calculate LP tokens minted or to be burned for depositing or
            removing `amounts` of coins
    @dev Includes fee.
    @param amounts Amounts of tokens being deposited or withdrawn
    @param deposit True if it is a deposit action, False if withdrawn.
    @return uint256 Amount of LP tokens deposited or withdrawn.
    """
    return staticcall self.VIEW.calc_token_amount(amounts, deposit, self)


@external
@view
def get_dy(i: uint256, j: uint256, dx: uint256) -> uint256:
    """
    @notice Get amount of coin[j] tokens received for swapping in dx amount of coin[i]
    @dev Includes fee.
    @param i index of input token. Check pool.coins(i) to get coin address at ith index
    @param j index of output token
    @param dx amount of input coin[i] tokens
    @return uint256 Exact amount of output j tokens for dx amount of i input tokens.
    """
    return staticcall self.VIEW.get_dy(i, j, dx, self)


@external
@view
def get_dx(i: uint256, j: uint256, dy: uint256, n_iter: uint256 = 5) -> uint256:
    """
    @notice Get amount of coin[i] tokens to input for swapping out dy amount
            of coin[j]
    @dev This is an approximate method, and returns estimates close to the input
         amount. Expensive to call on-chain.
    @param i index of input token. Check pool.coins(i) to get coin address at
           ith index
    @param j index of output token
    @param dy amount of input coin[j] tokens received
    @param n_iter number of iterations to run
    @return uint256 Approximate amount of input i tokens to get dy amount of j tokens.
    """
    return staticcall self.VIEW.get_dx(i, j, dy, self, n_iter)


@external
@view
@nonreentrant
def lp_price() -> uint256:
    """
    @notice Calculates the current price of the LP token w.r.t coin at the
            0th index
    @return uint256 LP price.
    """
    return 2 * self.virtual_price * isqrt(self.internal_price_oracle() * 10**18) // 10**18


@external
@view
@nonreentrant
def get_virtual_price() -> uint256:
    """
    @notice Calculates the current virtual price of the pool LP token.
    @dev Not to be confused with `self.virtual_price` which is a cached
         virtual price.
    @return uint256 Virtual Price.
    """

    return 10**18 * self._xcp(self.D, self.cached_price_scale) // self.totalSupply


@external
@view
@nonreentrant
def price_oracle() -> uint256:
    """
    @notice Returns the oracle price of the coin at index `k` w.r.t the coin
            at index 0.
    @dev The oracle is an exponential moving average, with a periodicity
         determined by `self.ma_time`. The aggregated prices are cached state
         prices (dy/dx) calculated AFTER the latest trade.
    @return uint256 Price oracle value of kth coin.
    """
    return self.internal_price_oracle()


@external
@view
@nonreentrant
def price_scale() -> uint256:
    """
    @notice Returns the price scale of the coin at index `k` w.r.t the coin
            at index 0.
    @dev Price scale determines the price band around which liquidity is
         concentrated.
    @return uint256 Price scale of coin.
    """
    return self.cached_price_scale


@external
@view
def fee() -> uint256:
    """
    @notice Returns the fee charged by the pool at current state.
    @dev Not to be confused with the fee charged at liquidity action, since
         there the fee is calculated on `xp` AFTER liquidity is added or
         removed.
    @return uint256 fee bps.
    """
    return self._fee(self._xp(self.balances, self.cached_price_scale))




@external
@view
def calc_token_fee(
    amounts: uint256[N_COINS], xp: uint256[N_COINS], donation: bool = False, deposit: bool = False
) -> uint256:
    """
    @notice Returns the fee charged on the given amounts for add_liquidity.
    @param amounts The amounts of coins being added to the pool (unscaled).
    @param xp The current balances of the pool multiplied by coin precisions.
    @param donation Whether the liquidity is a donation, if True only NOISE_FEE is charged.
    @param deposit Whether the liquidity is a deposit.
    @return uint256 Fee charged.
    """
    # last True is for from_view
    return self._calc_token_fee(amounts, xp, donation, deposit, True)


@view
@external
def A() -> uint256:
    """
    @notice Returns the current pool amplification parameter.
    @return uint256 A param.
    """
    return self._A_gamma()[0]


@view
@external
def gamma() -> uint256:
    """
    @notice Returns the current pool gamma parameter.
    @return uint256 gamma param.
    """
    return self._A_gamma()[1]


@view
@external
def mid_fee() -> uint256:
    """
    @notice Returns the current mid fee
    @return uint256 mid_fee value.
    """
    return self._unpack_3(self.packed_fee_params)[0]


@view
@external
def out_fee() -> uint256:
    """
    @notice Returns the current out fee
    @return uint256 out_fee value.
    """
    return self._unpack_3(self.packed_fee_params)[1]


@view
@external
def fee_gamma() -> uint256:
    """
    @notice Returns the current fee gamma
    @return uint256 fee_gamma value.
    """
    return self._unpack_3(self.packed_fee_params)[2]


@view
@external
def allowed_extra_profit() -> uint256:
    """
    @notice Returns the current allowed extra profit
    @return uint256 allowed_extra_profit value.
    """
    return self._unpack_3(self.packed_rebalancing_params)[0]


@view
@external
def adjustment_step() -> uint256:
    """
    @notice Returns the current adjustment step
    @return uint256 adjustment_step value.
    """
    return self._unpack_3(self.packed_rebalancing_params)[1]


@view
@external
def ma_time() -> uint256:
    """
    @notice Returns the current moving average time in seconds
    @dev To get time in seconds, the parameter is multipled by ln(2)
         One can expect off-by-one errors here.
    @return uint256 ma_time value.
    """
    return self._unpack_3(self.packed_rebalancing_params)[2] * 694 // 1000


@view
@external
def precisions() -> uint256[N_COINS]:  # <-------------- For by view contract.
    """
    @notice Returns the precisions of each coin in the pool.
    @return uint256[3] precisions of coins.
    """
    return PRECISIONS


@external
@view
def fee_calc(xp: uint256[N_COINS]) -> uint256:  # <----- For by view contract.
    """
    @notice Returns the fee charged by the pool at current state.
    @param xp The current balances of the pool multiplied by coin precisions.
    @return uint256 Fee value.
    """
    return self._fee(xp)


# ------------------------- AMM Admin Functions ------------------------------


@external
def ramp_A_gamma(
    future_A: uint256, future_gamma: uint256, future_time: uint256
):
    """
    @notice Initialise Ramping A and gamma parameter values linearly.
    @dev Only accessible by factory admin, and only
    @param future_A The future A value.
    @param future_gamma The future gamma value.
    @param future_time The timestamp at which the ramping will end.
    """
    self._check_admin()
    assert not self._is_ramping(), "!ramp"
    assert future_time > block.timestamp + MIN_RAMP_TIME - 1, "ramp time<min"

    A_gamma: uint256[2] = self._A_gamma()
    initial_A_gamma: uint256 = A_gamma[0] << 128
    initial_A_gamma = initial_A_gamma | A_gamma[1]

    assert future_A > MIN_A - 1, "A<min"
    assert future_A < MAX_A + 1, "A>max"
    assert future_gamma > MIN_GAMMA - 1, "gamma<min"
    assert future_gamma < MAX_GAMMA + 1, "gamme>max"

    ratio: uint256 = 10**18 * future_A // A_gamma[0]
    assert ratio < 10**18 * MAX_PARAM_CHANGE + 1, "A too high"
    assert ratio > 10**18 // MAX_PARAM_CHANGE - 1, "A too low"

    ratio = 10**18 * future_gamma // A_gamma[1]
    assert ratio < 10**18 * MAX_PARAM_CHANGE + 1, "gamma too high"
    assert ratio > 10**18 // MAX_PARAM_CHANGE - 1, "gamma too low"

    self.initial_A_gamma = initial_A_gamma
    self.initial_A_gamma_time = block.timestamp

    future_A_gamma: uint256 = future_A << 128
    future_A_gamma = future_A_gamma | future_gamma
    self.future_A_gamma_time = future_time
    self.future_A_gamma = future_A_gamma

    log RampAgamma(
        initial_A=A_gamma[0],
        future_A=future_A,
        initial_gamma=A_gamma[1],
        future_gamma=future_gamma,
        initial_time=block.timestamp,
        future_time=future_time
    )


@external
def stop_ramp_A_gamma():
    """
    @notice Stop Ramping A and gamma parameters immediately.
    @dev Only accessible by factory admin.
    """
    self._check_admin()

    A_gamma: uint256[2] = self._A_gamma()
    current_A_gamma: uint256 = A_gamma[0] << 128
    current_A_gamma = current_A_gamma | A_gamma[1]
    self.initial_A_gamma = current_A_gamma
    self.future_A_gamma = current_A_gamma
    self.initial_A_gamma_time = block.timestamp
    self.future_A_gamma_time = block.timestamp

    # ------ Now (block.timestamp < t1) is always False, so we return saved A.

    log StopRampA(current_A=A_gamma[0], current_gamma=A_gamma[1], time=block.timestamp)


@external
@nonreentrant
def apply_new_parameters(
    _new_mid_fee: uint256,
    _new_out_fee: uint256,
    _new_fee_gamma: uint256,
    _new_allowed_extra_profit: uint256,
    _new_adjustment_step: uint256,
    _new_ma_time: uint256,
):
    """
    @notice Commit new parameters.
    @dev Only accessible by factory admin.
    @param _new_mid_fee The new mid fee.
    @param _new_out_fee The new out fee.
    @param _new_fee_gamma The new fee gamma.
    @param _new_allowed_extra_profit The new allowed extra profit.
    @param _new_adjustment_step The new adjustment step.
    @param _new_ma_time The new ma time. ma_time is time_in_seconds/ln(2).
    """
    self._check_admin()

    # ----------------------------- Set fee params ---------------------------

    new_mid_fee: uint256 = _new_mid_fee
    new_out_fee: uint256 = _new_out_fee
    new_fee_gamma: uint256 = _new_fee_gamma

    current_fee_params: uint256[3] = self._unpack_3(self.packed_fee_params)

    if new_out_fee < MAX_FEE + 1:
        assert new_out_fee > MIN_FEE - 1, "!fee"
    else:
        new_out_fee = current_fee_params[1]

    if new_mid_fee > MAX_FEE:
        new_mid_fee = current_fee_params[0]
    assert new_mid_fee <= new_out_fee, "!mid-fee"

    if new_fee_gamma < 10**18:
        assert new_fee_gamma > 0, "!fee_gamma"
    else:
        new_fee_gamma = current_fee_params[2]

    self.packed_fee_params = self._pack_3([new_mid_fee, new_out_fee, new_fee_gamma])

    # ----------------- Set liquidity rebalancing parameters -----------------

    new_allowed_extra_profit: uint256 = _new_allowed_extra_profit
    new_adjustment_step: uint256 = _new_adjustment_step
    new_ma_time: uint256 = _new_ma_time

    current_rebalancing_params: uint256[3] = self._unpack_3(self.packed_rebalancing_params)

    if new_allowed_extra_profit > 10**18:
        new_allowed_extra_profit = current_rebalancing_params[0]

    if new_adjustment_step > 10**18:
        new_adjustment_step = current_rebalancing_params[1]

    if new_ma_time < 872542:  # <----- Calculated as: 7 * 24 * 60 * 60 / ln(2)
        assert new_ma_time > 86, "MA<60/ln(2)"
    else:
        new_ma_time = current_rebalancing_params[2]

    self.packed_rebalancing_params = self._pack_3(
        [new_allowed_extra_profit, new_adjustment_step, new_ma_time]
    )

    # ---------------------------------- LOG ---------------------------------

    log NewParameters(
        mid_fee=new_mid_fee,
        out_fee=new_out_fee,
        fee_gamma=new_fee_gamma,
        allowed_extra_profit=new_allowed_extra_profit,
        adjustment_step=new_adjustment_step,
        ma_time=new_ma_time
    )


@external
def set_donation_duration(duration: uint256):
    """
    @notice Set the donation duration.
    @param duration The new donation duration.
    @dev The time required for donations to fully release from locked state.
    """
    self._check_admin()
    assert duration > 0, "!duration"
    self.donation_duration = duration
    log SetDonationDuration(duration=duration)


@external
def set_donation_protection_params(
    _period: uint256,
    _threshold: uint256,
    _max_shares_ratio: uint256,
):
    """
    @notice Set donation protection parameters.
    @param _period The new donation protection period in seconds.
    @param _threshold The new donation protection threshold with 10**18 precision.
    @param _max_shares_ratio The new maximum number of shares.
    @dev _threshold = 30 * 10**18//100 means 30%
    @dev _max_shares_ratio = 10 * 10**18//100 means 10%
    """

    self._check_admin()
    assert _period > 0, "!period"
    assert _threshold > 0, "!threshold"
    assert _max_shares_ratio > 0, "!max_shares"
    self.donation_protection_period = _period
    self.donation_protection_lp_threshold = _threshold
    self.donation_shares_max_ratio = _max_shares_ratio
    log SetDonationProtection(
        donation_protection_period=_period,
        donation_protection_lp_threshold=_threshold,
        donation_shares_max_ratio=_max_shares_ratio
        )


@external
def set_admin_fee(admin_fee: uint256):
    """
    @notice Set the admin fee.
    @param admin_fee The new admin fee.
    @dev The admin fee is a percentage of the profits that are
         claimed by the admin. The fee is set in bps.
    """

    self._check_admin()
    assert admin_fee <= MAX_ADMIN_FEE, "admin_fee>MAX"

    self.admin_fee = admin_fee
    log SetAdminFee(admin_fee=admin_fee)

@external
def set_periphery(views: Views, math: Math):
    """
    @notice Set the view contract.
    @param views The new view contract.
    @dev This function is used to set the view contract that will be used
         to calculate the prices and fees.
    """
    self._check_admin()
    # at least one of the two must be set
    assert views != empty(Views) or math != empty(Math), "!contract"

    if views != empty(Views):
        self.VIEW = views
    if math != empty(Math):
        self.MATH = math
    log SetPeriphery(views=views, math=math)

Settings
{
  "outputSelection": {
    "<unknown>": [
      "evm.bytecode",
      "evm.deployedBytecode",
      "abi"
    ]
  },
  "search_paths": [
    "."
  ]
}

Contract Security Audit

Contract ABI

API
[{"anonymous":false,"inputs":[{"indexed":false,"name":"views","type":"address"},{"indexed":false,"name":"math","type":"address"}],"name":"SetPeriphery","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"},{"indexed":true,"name":"receiver","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"buyer","type":"address"},{"indexed":false,"name":"sold_id","type":"uint256"},{"indexed":false,"name":"tokens_sold","type":"uint256"},{"indexed":false,"name":"bought_id","type":"uint256"},{"indexed":false,"name":"tokens_bought","type":"uint256"},{"indexed":false,"name":"fee","type":"uint256"},{"indexed":false,"name":"price_scale","type":"uint256"}],"name":"TokenExchange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"provider","type":"address"},{"indexed":true,"name":"receiver","type":"address"},{"indexed":false,"name":"token_amounts","type":"uint256[2]"},{"indexed":false,"name":"fee","type":"uint256"},{"indexed":false,"name":"token_supply","type":"uint256"},{"indexed":false,"name":"price_scale","type":"uint256"}],"name":"AddLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"donor","type":"address"},{"indexed":false,"name":"token_amounts","type":"uint256[2]"}],"name":"Donation","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"provider","type":"address"},{"indexed":false,"name":"token_amounts","type":"uint256[2]"},{"indexed":false,"name":"token_supply","type":"uint256"}],"name":"RemoveLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"provider","type":"address"},{"indexed":false,"name":"token_amount","type":"uint256"},{"indexed":false,"name":"coin_index","type":"uint256"},{"indexed":false,"name":"coin_amount","type":"uint256"},{"indexed":false,"name":"approx_fee","type":"uint256"},{"indexed":false,"name":"packed_price_scale","type":"uint256"}],"name":"RemoveLiquidityOne","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"provider","type":"address"},{"indexed":false,"name":"lp_token_amount","type":"uint256"},{"indexed":false,"name":"token_amounts","type":"uint256[2]"},{"indexed":false,"name":"approx_fee","type":"uint256"},{"indexed":false,"name":"price_scale","type":"uint256"}],"name":"RemoveLiquidityImbalance","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"mid_fee","type":"uint256"},{"indexed":false,"name":"out_fee","type":"uint256"},{"indexed":false,"name":"fee_gamma","type":"uint256"},{"indexed":false,"name":"allowed_extra_profit","type":"uint256"},{"indexed":false,"name":"adjustment_step","type":"uint256"},{"indexed":false,"name":"ma_time","type":"uint256"}],"name":"NewParameters","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"initial_A","type":"uint256"},{"indexed":false,"name":"future_A","type":"uint256"},{"indexed":false,"name":"initial_gamma","type":"uint256"},{"indexed":false,"name":"future_gamma","type":"uint256"},{"indexed":false,"name":"initial_time","type":"uint256"},{"indexed":false,"name":"future_time","type":"uint256"}],"name":"RampAgamma","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"current_A","type":"uint256"},{"indexed":false,"name":"current_gamma","type":"uint256"},{"indexed":false,"name":"time","type":"uint256"}],"name":"StopRampA","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"admin","type":"address"},{"indexed":false,"name":"tokens","type":"uint256[2]"}],"name":"ClaimAdminFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"duration","type":"uint256"}],"name":"SetDonationDuration","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"donation_protection_period","type":"uint256"},{"indexed":false,"name":"donation_protection_lp_threshold","type":"uint256"},{"indexed":false,"name":"donation_shares_max_ratio","type":"uint256"}],"name":"SetDonationProtection","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"admin_fee","type":"uint256"}],"name":"SetAdminFee","type":"event"},{"inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dx","type":"uint256"},{"name":"min_dy","type":"uint256"}],"name":"exchange","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dx","type":"uint256"},{"name":"min_dy","type":"uint256"},{"name":"receiver","type":"address"}],"name":"exchange","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dx","type":"uint256"},{"name":"min_dy","type":"uint256"}],"name":"exchange_received","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dx","type":"uint256"},{"name":"min_dy","type":"uint256"},{"name":"receiver","type":"address"}],"name":"exchange_received","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"amounts","type":"uint256[2]"},{"name":"min_mint_amount","type":"uint256"}],"name":"add_liquidity","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"amounts","type":"uint256[2]"},{"name":"min_mint_amount","type":"uint256"},{"name":"receiver","type":"address"}],"name":"add_liquidity","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"amounts","type":"uint256[2]"},{"name":"min_mint_amount","type":"uint256"},{"name":"receiver","type":"address"},{"name":"donation","type":"bool"}],"name":"add_liquidity","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"amount","type":"uint256"},{"name":"min_amounts","type":"uint256[2]"}],"name":"remove_liquidity","outputs":[{"name":"","type":"uint256[2]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"amount","type":"uint256"},{"name":"min_amounts","type":"uint256[2]"},{"name":"receiver","type":"address"}],"name":"remove_liquidity","outputs":[{"name":"","type":"uint256[2]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"token_amount","type":"uint256"},{"name":"i","type":"uint256"},{"name":"amount_i","type":"uint256"},{"name":"min_amount_j","type":"uint256"}],"name":"remove_liquidity_fixed_out","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"token_amount","type":"uint256"},{"name":"i","type":"uint256"},{"name":"amount_i","type":"uint256"},{"name":"min_amount_j","type":"uint256"},{"name":"receiver","type":"address"}],"name":"remove_liquidity_fixed_out","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"lp_token_amount","type":"uint256"},{"name":"i","type":"uint256"},{"name":"min_amount","type":"uint256"}],"name":"remove_liquidity_one_coin","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"lp_token_amount","type":"uint256"},{"name":"i","type":"uint256"},{"name":"min_amount","type":"uint256"},{"name":"receiver","type":"address"}],"name":"remove_liquidity_one_coin","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"user_supply","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"lp_token_amount","type":"uint256"},{"name":"i","type":"uint256"},{"name":"amount_i","type":"uint256"}],"name":"calc_withdraw_fixed_out","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"lp_token_amount","type":"uint256"},{"name":"i","type":"uint256"}],"name":"calc_withdraw_one_coin","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fee_receiver","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"admin","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"amounts","type":"uint256[2]"},{"name":"deposit","type":"bool"}],"name":"calc_token_amount","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dx","type":"uint256"}],"name":"get_dy","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dy","type":"uint256"}],"name":"get_dx","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"},{"name":"dy","type":"uint256"},{"name":"n_iter","type":"uint256"}],"name":"get_dx","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lp_price","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"get_virtual_price","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"price_oracle","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"price_scale","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fee","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"amounts","type":"uint256[2]"},{"name":"xp","type":"uint256[2]"}],"name":"calc_token_fee","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"amounts","type":"uint256[2]"},{"name":"xp","type":"uint256[2]"},{"name":"donation","type":"bool"}],"name":"calc_token_fee","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"amounts","type":"uint256[2]"},{"name":"xp","type":"uint256[2]"},{"name":"donation","type":"bool"},{"name":"deposit","type":"bool"}],"name":"calc_token_fee","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"A","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gamma","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mid_fee","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"out_fee","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fee_gamma","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allowed_extra_profit","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"adjustment_step","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ma_time","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"precisions","outputs":[{"name":"","type":"uint256[2]"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"xp","type":"uint256[2]"}],"name":"fee_calc","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"future_A","type":"uint256"},{"name":"future_gamma","type":"uint256"},{"name":"future_time","type":"uint256"}],"name":"ramp_A_gamma","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stop_ramp_A_gamma","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_new_mid_fee","type":"uint256"},{"name":"_new_out_fee","type":"uint256"},{"name":"_new_fee_gamma","type":"uint256"},{"name":"_new_allowed_extra_profit","type":"uint256"},{"name":"_new_adjustment_step","type":"uint256"},{"name":"_new_ma_time","type":"uint256"}],"name":"apply_new_parameters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"duration","type":"uint256"}],"name":"set_donation_duration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_period","type":"uint256"},{"name":"_threshold","type":"uint256"},{"name":"_max_shares_ratio","type":"uint256"}],"name":"set_donation_protection_params","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"admin_fee","type":"uint256"}],"name":"set_admin_fee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"views","type":"address"},{"name":"math","type":"address"}],"name":"set_periphery","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"MATH","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VIEW","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"arg0","type":"uint256"}],"name":"coins","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"last_prices","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"last_timestamp","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initial_A_gamma","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initial_A_gamma_time","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"future_A_gamma","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"future_A_gamma_time","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"donation_shares","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"donation_shares_max_ratio","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"donation_duration","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"last_donation_release_ts","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"donation_protection_expiry_ts","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"donation_protection_period","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"donation_protection_lp_threshold","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"arg0","type":"uint256"}],"name":"balances","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"D","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"xcp_profit","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"xcp_profit_a","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"virtual_price","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"packed_rebalancing_params","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"packed_fee_params","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"admin_fee","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"arg0","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"arg0","type":"address"},{"name":"arg1","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"_name","type":"string"},{"name":"_symbol","type":"string"},{"name":"_coins","type":"address[2]"},{"name":"_math","type":"address"},{"name":"_salt","type":"bytes32"},{"name":"packed_precisions","type":"uint256"},{"name":"packed_gamma_A","type":"uint256"},{"name":"packed_fee_params","type":"uint256"},{"name":"packed_rebalancing_params","type":"uint256"},{"name":"initial_price","type":"uint256"}],"outputs":[],"stateMutability":"nonpayable","type":"constructor"}]

0x615c47515034610483576020615fe75f395f51602081615fe7015f395f51604081116104835750606081615fe7016060395060206160075f395f51602081615fe7015f395f51602081116104835750604081615fe70160c0395060206160275f395f518060a01c610483576101005260206160475f395f518060a01c610483576101205260206160675f395f518060a01c61048357610140527335048188c02cbc9239e1e5ecb3761ef9dfdcd31f6001557379839c2d74531a8222c0f555865aac1834e82e515f555f60195533615ba7526020606051015f81601f0160051c6003811161048357801561010d57905b8060051b606001518160051b60a001615b2701526001018181186100ee575b50505060c051615c275260e051615c475261010051615b675261012051615b875260206160a760403961014161016061045e565b6101608051615b27526020810151615b47525060206160c76040396101676101a061045e565b6101a06040816101605e506402540be4006101605110156101fa576020806102005260096101a0527f67616d6d613c4d494e00000000000000000000000000000000000000000000006101c0526101a08161020001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101e052806004016101fcfd5b6702c2fd72164d8000610160511115610285576020806102005260096101a0527f67616d6d613e4d415800000000000000000000000000000000000000000000006101c0526101a08161020001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101e052806004016101fcfd5b614e2061018051101561030a576020806102005260056101a0527f413c4d494e0000000000000000000000000000000000000000000000000000006101c0526101a08161020001602582825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101e052806004016101fcfd5b6305f5e100610180511115610391576020806102005260056101a0527f413e4d41580000000000000000000000000000000000000000000000000000006101c0526101a08161020001602582825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101e052806004016101fcfd5b60206160c75f395f5160065560206160c75f395f5160085560206161075f395f5160175560206160e75f395f5160185560206161275f395f5160025560206161275f395f5160035560206161275f395f5160045542600555670de0b6b3a764000060155562093a80600c555f600e55610258600f556702c68af0bb14000060105567016345785d8a0000600b55305f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f6101a05260206101a0a3615b2761048761000039615c67610000f35b6fffffffffffffffffffffffffffffffff60405116815260405160801c602082015250565b5f80fd5f3560e01c60026048820660011b615a9701601e395f51565b635b41b908811861003757608436103417615a935733610900526100aa565b63d5f8da308118612c4d57606436103417615a93576020610059610760612f24565b6107606040816108405e50606060046108803760a06108406103805e6100806107a0614e6a565b6107a0f35b63a64833a081186101785760a436103417615a93576084358060a01c615a9357610900525b5f5c600114615a935760015f5d600435604052604435606052336080525f60a0526100d6610940612c51565b610940516109205260406004610600376109205161064052606435610660526101006109a0613df8565b6109a06060816109405e5060243560405261094051606052610900516080526101276143f8565b337f143f1f8e861fbdeddd5b46e844b7d3ac7b86a122f36e8c463859ee6811b1f29c6004356109a052610920516109c0526024356109e0526060610940610a005e60c06109a0a260206109405f5f5df35b633dd654788118612c4d5734615a935760175460405260206040f35b6329b244bb8118612c4d57608436103417615a935733610900526101d8565b63767691e781186102a75760a436103417615a93576084358060a01c615a9357610900525b5f5c600114615a935760015f5d60043560405260443560605233608052600160a052610205610940612c51565b6109405161092052604060046106003761092051610640526064356106605261022f6109a0613df8565b6109a06060816109405e5060243560405261094051606052610900516080526102566143f8565b337f143f1f8e861fbdeddd5b46e844b7d3ac7b86a122f36e8c463859ee6811b1f29c6004356109a052610920516109c0526024356109e0526060610940610a005e60c06109a0a260206109405f5f5df35b6348155d27811861030757608436103417615a93576064356040525b60206001546333e39968606052606060046080373060e052604051610100526020606060a4607c845afa6102f9573d5f5f3e3d5ffd5b60203d10615a935760609050f35b63f9ed95978118612c4d5734615a935760095460405260206040f35b630b4c7e4d811861034757606436103417615a935733610600525f610620526103f1565b63083812e58118612c4d5734615a9357602060175460405261036960606130f4565b6060602081019050f35b630c3e4b5481186103a157608436103417615a93576064358060a01c615a9357610600525f610620526103f1565b63c146bf948118612c4d5734615a935760045460405260206040f35b63865147388118612c4d5760a436103417615a93576064358060a01c615a9357610600526084358060011c615a9357610620525b5f5c600114615a935760015f5d600435602435808201828110615a93579050905061048e576020806106a0526008610640527f21616d6f756e747300000000000000000000000000000000000000000000000061066052610640816106a001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610680528060040161069cfd5b6011546106405260125461066052604036610680376011546106c0526012546106e0525f6002905b8061070052610700516002811015615a935760051b600401351561055b5761070051604052610700516002811015615a935760051b60040135606052336080525f60a052610505610720612c51565b61072051610700516002811015615a935760051b6106800152610700516002811015615a935760051b6106c0018051610700516002811015615a935760051b6106800151808201828110615a9357905090508152505b6001018181186104b65750506002546107005260406106c060405e6107005160805261058861076061307d565b6107606040816107205e50604061064060405e610700516080526105ad6107a061307d565b6107a06040816107605e506013546105c457426009555b6105cf6107e0612f24565b6107e06040816107a05e5060406107a060405e604061076060805e6105f5610800614527565b610800516107e0525f5463e68647666108205260406107a06108405e60406107206108805e5f6108c052602061082060a461083c845afa610638573d5f5f3e3d5ffd5b60203d10615a935761082090505161080052601d54610820525f610840526107e051156106a7576108205161080051808202811583838304141715615a9357905090506107e0518015615a93578082049050905061082051808203828111615a935790509050610840526106c9565b61080051604052610700516060526106c06108606133dd565b61086051610840525b61084051610749576020806108c052600e610860527f6e6f7468696e67206d696e74656400000000000000000000000000000000000061088052610860816108c001602e82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06108a052806004016108bcfd5b5f610860526107e05115610b3c5760406106806101605e60406107206101a05e610620516101e0526001610200525f6102205261078761088061458c565b6108805161084051808202811583838304141715615a9357905090506402540be4008104905060018101818110615a93579050610860526108405161086051808203828111615a93579050905061084052610620516109045761084051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790506108205161084051808201828110615a9357905090508015615a93578082049050905061088052610880511561083c57600a54151561083e565b5f5b156108e257600f546108a052610880516108a051808202811583838304141715615a9357905090506010548015615a9357808204905090506108a051808281188284100218905090506108c052600e5442808281188284110218905090506108e0526108e0516108c051808201828110615a935790509050426108a051808201828110615a935790509050808281188284100218905090506109005261090051600e555b61060051604052610840516060526108fb6108a061485b565b6108a050610b0a565b6106005115610985576020806108e0526010610880527f6e6f6e7a65726f207265636569766572000000000000000000000000000000006108a052610880816108e001603082825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06108c052806004016108dcfd5b600a5461084051808201828110615a93579050905061088052600b5461088051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790506108205161084051808201828110615a9357905090508015615a9357808204905090501115610a64576020806109005260136108a0527f646f6e6174696f6e2061626f76652063617021000000000000000000000000006108c0526108a08161090001603382825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06108e052806004016108fcfd5b5f604052610a736108c06132a7565b6108c051600c54808202811583838304141715615a935790509050610880518015615a9357808204905090506108a052426108a051808203828111615a935790509050600d5561088051600a55601d5461084051808201828110615a935790509050601d55337fc05458c16b884817a70d3d18223db5fe4adb4cb541a5573bef0daae7a6f2054260406106806108c05e60406108c0a25b60406107a06101005e60406107206101405e6108005161018052610b2f610880613517565b6108805161070052610b85565b61080051601355670de0b6b3a7640000601655670de0b6b3a7640000601455670de0b6b3a76400006015556106005160405261084051606052610b8061088061485b565b610880505b604435610840511015610c0a576020806108e0526008610880527f736c6970706167650000000000000000000000000000000000000000000000006108a052610880816108e001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06108c052806004016108dcfd5b61060051337f0e1f3c59f25a027e14a3f55c68245d22089c42b1dcd09f123a11d4af3c0d6f7260406106806108805e610860516108c0526108205161084051808201828110615a9357905090506108e052610700516109005260a0610880a360206108405f5f5df35b635b36389c8118610c9257606436103417615a9357336102a052610cdd565b6392526c0c8118612c4d5734615a93576020601854604052610cb460606130f4565b6060f35b633eb1719f8118610f4657608436103417615a93576064358060a01c615a93576102a0525b5f5c600114615a935760015f5d601d546102c05233604052600435606052610d066102e06148cd565b6102e0506040366102e037601354610320526102c05160043518610d65575f6002905b8061034052610340516002811015615a935760110154610340516002811015615a935760051b6102e00152600101818118610d29575050610e71565b5f6002905b8061034052610340516002811015615a935760110154600435808202811583838304141715615a9357905090506102c0518015615a935780820490509050610340516002811015615a935760051b6102e00152610340516002811015615a935760051b60240135610340516002811015615a935760051b6102e001511015610e64576020806103c0526008610360527f736c69707061676500000000000000000000000000000000000000000000000061038052610360816103c001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06103a052806004016103bcfd5b600101818118610d6a5750505b610320516102c05161032051600435808202811583838304141715615a93579050905004808203828111615a9357905090506013555f6002905b806103405261034051604052610340516002811015615a935760051b6102e001516060526102a051608052610ede6143f8565b600101818118610eab575050337fdd3c0336a16f1b64f172b7bb0dad5b2b3c7c76f91e8c4aafd6aae60dce80015360406102e06103405e6102c051600435808203828111615a935790509050610380526060610340a2610f3c61493f565b60406102e05f5f5df35b637c71109f8118612c4d5734615a9357600d5460405260206040f35b63512d63658118610f8157608436103417615a935733610a2052611072565b6323b872dd8118612c4d57606436103417615a93576004358060a01c615a9357610180526024358060a01c615a93576101a052601c610180516020525f5260405f2080336020525f5260405f209050546101c0527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101c0511461102a5761018051604052336060526101c051604435808203828111615a93579050905060805261102a6156d3565b604061018060405e604435608052611040615728565b60016101e05260206101e0f35b63b2f9173e8118612c4d5760a436103417615a93576084358060a01c615a9357610a20525b5f5c600114615a935760015f5d60206080600461076037610a20516107e05261109c610a406153fd565b610a405f5f5df35b63f1dc3cc981186110c357606436103417615a935733610a2052611116565b634903b0d18118612c4d57602436103417615a93576004356002811015615a93576011015460405260206040f35b630fbcee6e8118612c4d57608436103417615a93576064358060a01c615a9357610a20525b5f5c600114615a935760015f5d6020600435610760526024358060010360018111615a93579050610780525f6107a0526044356107c052610a20516107e052611160610a406153fd565b610a405f5f5df35b631a2430cc8118612c4d5734615a9357601d54600a54808203828111615a93579050905060405260206040f35b634fb08c5e81186111fc57604436103417615a935760206111b7610760612f24565b6107606040816108405e50600435610880526024358060010360018111615a935790506108a0525f6108c05260a06108406103805e6111f76107a0614e6a565b6107a0f35b63244c7c2e8118612c4d5734615a93576112146159af565b61121f6101a0612f24565b6101a06040816101605e506101605160801b6101a052610180516101a051176101a0526101a0516006556101a05160085542600755426009557f5f0e7fba3d100c9e19446e1c92fe436f0a9a22fe99669360e4fdd6d3de2fc28460406101606101c05e426102005260606101c0a1005b63a9059cbb81186112d957604436103417615a93576004358060a01c615a93576101805233604052610180516060526024356080526112cc615728565b60016101a05260206101a0f35b6386fc88d381186113045734615a93575f5c600114615a935760206112ff610160615833565b610160f35b630b7b594b8118612c4d5734615a935760155460405260206040f35b63095ea7b38118612c4d57604436103417615a93576004358060a01c615a935760c0523360405260c05160605260243560805261135b6156d3565b600160e052602060e0f35b63cab4d3db81186113cb5734615a935760206020615ba75f395f5163cab4d3db604052602060406004605c845afa6113a0573d5f5f3e3d5ffd5b3d602081183d602010021880604001606011615a93576040518060a01c615a93576080525060809050f35b632a3f192b8118612c4d5734615a935760015460405260206040f35b63f851a4408118612c4d5734615a935760206020615ba75f395f5163f851a440604052602060406004605c845afa611421573d5f5f3e3d5ffd5b3d602081183d602010021880604001606011615a93576040518060a01c615a93576080525060809050f35b63ed8e84f381186114b257606436103417615a93576044358060011c615a9357604052602060015463bc5bc6b76060526040600460803760405160c0523060e052602060606084607c845afa6114a4573d5f5f3e3d5ffd5b60203d10615a935760609050f35b6306fdde038118612c4d5734615a9357602080604052806040016060615bc782398051806020830101601f825f03163682375050601f19601f825160200101169050810190506040f35b63556d6e9f811861154e57606436103417615a93576020600154633bb1f8c1604052606060046060373060c052602060406084605c845afa611540573d5f5f3e3d5ffd5b60203d10615a935760409050f35b63c6610657811861158457602436103417615a935760206004356002811015615a935760051b604001615b270160403960206040f35b63e89876ff8118612c4d5734615a935760075460405260206040f35b6337ed3a7a81186115bf57606436103417615a935760056040526102c3565b630c46b72a8118612c4d5734615a935760165460405260206040f35b6354f0f7d581186117405734615a93575f5c600114615a93576016548060011b818160011c18615a93579050611612610160615833565b61016051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790508060b571010000000000000000000000000000000000821061165d578160801c91508060401b90505b6901000000000000000000821061167b578160401c91508060201b90505b650100000000008210611695578160201c91508060101b90505b630100000082106116ad578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c90508083048082811882841002189050905090509050808202811583838304141715615a935790509050670de0b6b3a764000081049050610180526020610180f35b63b9e8c9fd8118612c4d5734615a93575f5c600114615a935760025460405260206040f35b63bb7b8b8081186117ce5734615a93575f5c600114615a935760135460405260025460605261179460806133dd565b608051670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050601d548015615a93578082049050905060a052602060a0f35b63f446c1d081186117f05734615a935760206117eb610120612f24565b610120f35b634d23bfa08118612c4d5734615a935760055460405260206040f35b63ddca3f438118612c4d5734615a9357602060115460405260125460605260025460805261183b61016061307d565b6101606040816101c05e5060406101c060605e6118596101a0613130565b6101a0f35b63bcc8342e811861187f57608436103417615a935760403661038037611923565b63fe192e9e8118612c4d5734615a935760105460405260206040f35b63326cc61781186118c95760a436103417615a93576084358060011c615a9357610380525f6103a052611923565b6349fe9e778118612c4d5734615a935760206017546040526118eb60606130f4565b6060f35b6357832be68118612c4d5760c436103417615a93576084358060011c615a93576103805260a4358060011c615a93576103a0525b60206040600461016037604060446101a03760406103806101e05e60016102205261194f6103c061458c565b6103c0f35b63b13739298118612c4d5734615a93576020611971610120612f24565b610120602081019050f35b63ee8de6758118612c4d5734615a9357602060185460405261199e60606130f4565b6060602081019050f35b6372d4f0e281186119d45734615a935760206018546040526119ca60606130f4565b6060604081019050f35b6309c3da6a8118611a1f5734615a93576017546040526119f460606130f4565b6060604081019050516102b68102816102b6820418615a935790506103e88104905060c052602060c0f35b630f529ba28118612c4d5734615a935760135460405260206040f35b633620604b8118612c4d5734615a93576040615b2760403960406040f35b6380823d9e8118611a8757604436103417615a9357602060406004606037611a82610160613130565b610160f35b63dd62ed3e8118612c4d57604436103417615a93576004358060a01c615a93576040526024358060a01c615a9357606052601c6040516020525f5260405f20806060516020525f5260405f2090505460805260206080f35b635e248072811861218757606436103417615a9357611afc6159af565b611b076101606130e8565b6101605115611b88576020806101e0526005610180527f2172616d700000000000000000000000000000000000000000000000000000006101a052610180816101e001602582825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101c052806004016101dcfd5b42620151808101818110615a9357905060018103818111615a9357905060443511611c25576020806101c052600d610160527f72616d702074696d653c6d696e0000000000000000000000000000000000000061018052610160816101c001602d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101a052806004016101bcfd5b611c306101a0612f24565b6101a06040816101605e506101605160801b6101a052610180516101a051176101a052614e206004351015611cd7576020806102205260056101c0527f413c6d696e0000000000000000000000000000000000000000000000000000006101e0526101c08161022001602582825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610200528060040161021cfd5b6305f5e1006004351115611d5d576020806102205260056101c0527f413e6d61780000000000000000000000000000000000000000000000000000006101e0526101c08161022001602582825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610200528060040161021cfd5b6402540be4006024351015611de4576020806102205260096101c0527f67616d6d613c6d696e00000000000000000000000000000000000000000000006101e0526101c08161022001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610200528060040161021cfd5b6702c2fd72164d80006024351115611e6e576020806102205260096101c0527f67616d6d653e6d617800000000000000000000000000000000000000000000006101e0526101c08161022001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610200528060040161021cfd5b600435670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610160518015615a9357808204905090506101c052678ac7230489e800006101c0511115611f2f5760208061024052600a6101e0527f4120746f6f206869676800000000000000000000000000000000000000000000610200526101e08161024001602a82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610220528060040161023cfd5b67016345785d8a00006101c0511015611fba576020806102405260096101e0527f4120746f6f206c6f770000000000000000000000000000000000000000000000610200526101e08161024001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610220528060040161023cfd5b602435670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610180518015615a9357808204905090506101c052678ac7230489e800006101c051111561207b5760208061024052600e6101e0527f67616d6d6120746f6f2068696768000000000000000000000000000000000000610200526101e08161024001602e82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610220528060040161023cfd5b67016345785d8a00006101c05110156121065760208061024052600d6101e0527f67616d6d6120746f6f206c6f7700000000000000000000000000000000000000610200526101e08161024001602d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610220528060040161023cfd5b6101a0516006554260075560043560801b6101e0526024356101e051176101e0526044356009556101e0516008557fe35f0559b0642164e286b30df2077ec3a05426617a25db7578fd20ba39a6cd0561016051610200526004356102205261018051610240526024356102605242610280526044356102a05260c0610200a1005b63d046b4ca8118612c4d5734615a9357600e5460405260206040f35b636dbcf350811861253d5760c436103417615a93575f5c600114615a935760015f5d6121cd6159af565b60606004610160376018546040526121e66102206130f4565b6102206060816101c05e506402540be40061018051111561220e576101e05161018052612294565b6207a12061018051101561229457602080610280526004610220527f2166656500000000000000000000000000000000000000000000000000000000610240526102208161028001602482825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610260528060040161027cfd5b6402540be40161016051106122ac576101c051610160525b6101805161016051111561233257602080610280526008610220527f216d69642d666565000000000000000000000000000000000000000000000000610240526102208161028001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610260528060040161027cfd5b670de0b6b3a763ffff6101a051111561235257610200516101a0526123d2565b6101a0516123d25760208061028052600a610220527f216665655f67616d6d6100000000000000000000000000000000000000000000610240526102208161028001602a82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610260528060040161027cfd5b606061016060405e6123e5610220615a7d565b6102205160185560606064610220376017546040526124056102e06130f4565b6102e06060816102805e50670de0b6b3a7640001610220511061242b5761028051610220525b670de0b6b3a76400016102405110612446576102a051610240525b620d505d610260511115612461576102c051610260526124e5565b60576102605110156124e55760208061034052600b6102e0527f4d413c36302f6c6e283229000000000000000000000000000000000000000000610300526102e08161034001602b82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610320528060040161033cfd5b606061022060405e6124f86102e0615a7d565b6102e0516017557fa32137411fc7c20db359079cd84af0e2cad58cd7a182a8a5e23e08e554e88bf060606101606102e05e60606102206103405e60c06102e0a15f5f5d005b6354fd4d508118612c4d5734615a935760208060805260076040527f76322e312e306400000000000000000000000000000000000000000000000000606052604081608001602782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506080f35b63f5b2f0168118612c4d57602436103417615a93576125cf6159af565b60043561264e576020806101c0526009610160527f216475726174696f6e000000000000000000000000000000000000000000000061018052610160816101c001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101a052806004016101bcfd5b600435600c557fb2cf7972e8e7c2db8a62b4c568cf133a24bf5910b2603ad8811e6bfc9a865322600435610160526020610160a1005b638325c0028118612c4d57606436103417615a93576126a16159af565b600435612720576020806101c0526007610160527f21706572696f640000000000000000000000000000000000000000000000000061018052610160816101c001602782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101a052806004016101bcfd5b60243561279f576020806101c052600a610160527f217468726573686f6c640000000000000000000000000000000000000000000061018052610160816101c001602a82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101a052806004016101bcfd5b60443561281e576020806101c052600b610160527f216d61785f73686172657300000000000000000000000000000000000000000061018052610160816101c001602b82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101a052806004016101bcfd5b600435600f55602435601055604435600b557f6db4ade9cd8d6e671d6d713ab38d8889f9e3d4bbb319ca3389a516cf4efcf19d60606004610160376060610160a1005b633217902f8118612c4d57602436103417615a935761287e6159af565b6402540be4006004351115612905576020806101c052600d610160527f61646d696e5f6665653e4d41580000000000000000000000000000000000000061018052610160816101c001602d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101a052806004016101bcfd5b6004356019557f2f0d0ace1d699b471d7b39522b5c8aae053bce1b422b7a4fe8f09bd6562a4b74600435610160526020610160a1005b636fe26a348118612c4d57604436103417615a93576004358060a01c615a9357610160526024358060a01c615a9357610180526129766159af565b610160511561298657600161298d565b6101805115155b612a09576020806102005260096101a0527f21636f6e747261637400000000000000000000000000000000000000000000006101c0526101a08161020001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101e052806004016101fcfd5b6101605115612a1a57610160516001555b6101805115612a2a57610180515f555b7f567da2faedc99f951794c7dc379b4906dcdd1d016fedb16916d1e9623901f21660406101606101a05e60406101a0a1005b63ed6c15468118612c4d5734615a93575f5460405260206040f35b63c45a01558118612a955734615a93576020615ba760403960206040f35b637ba1a74d8118612ab15734615a935760145460405260206040f35b63e36164058118612c4d5734615a935760185460405260206040f35b63204fe3d58118612c4d5734615a935760065460405260206040f35b63f30cfad58118612b055734615a935760085460405260206040f35b6318160ddd8118612c4d5734615a9357601d5460405260206040f35b63a3bdf1b78118612c4d5734615a9357600a5460405260206040f35b633d2699f28118612c4d5734615a9357600b5460405260206040f35b630decf4a28118612c4d5734615a9357600c5460405260206040f35b631f88619c8118612c4d5734615a9357600f5460405260206040f35b63fee3f7f98118612c4d5734615a935760195460405260206040f35b6395d89b418118612c4d5734615a9357602080604052806040016040615c2782398051806020830101601f825f03163682375050601f19601f825160200101169050810190506040f35b63313ce5678118612c4d5734615a9357601260405260206040f35b6370a082318118612c4d57602436103417615a93576004358060a01c615a9357604052601b6040516020525f5260405f205460605260206060f35b5f5ffd5b60206040516002811015615a935760051b604001615b27015f395f516370a0823160e0523061010052602060e0602460fc845afa612c91573d5f5f3e3d5ffd5b60203d10615a935760e090505160c05260a05115612d815760c0516040516002811015615a935760110154808203828111615a93579050905060e05260605160e0511015612d5157602080610160526006610100527f21636f696e730000000000000000000000000000000000000000000000000000610120526101008161016001602682825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610140528060040161015cfd5b6040516002811015615a9357601101805460e051808201828110615a93579050905081555060e051815250612f22565b60206040516002811015615a935760051b604001615b27015f395f516323b872dd60e05260805161010052306101205260605161014052602060e0606460fc5f855af1612dd0573d5f5f3e3d5ffd5b3d612de757803b15615a9357600161016052612e0f565b3d602081183d60201002188060e00161010011615a935760e0518060011c615a935761016052505b610160905051612e91576020806101e052600d610180527f217472616e7366657246726f6d000000000000000000000000000000000000006101a052610180816101e001602d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101c052806004016101dcfd5b60206040516002811015615a935760051b604001615b27015f395f516370a082316101005230610120526020610100602461011c845afa612ed4573d5f5f3e3d5ffd5b60203d10615a935761010090505160c051808203828111615a93579050905060e0526040516002811015615a9357601101805460e051808201828110615a93579050905081555060e0518152505b565b6009546040526008546060526fffffffffffffffffffffffffffffffff6060511660805260605160801c60a05260405142101561306d5760065460c05260075460e05260405160e051808203828111615a9357905090506040524260e051808203828111615a93579050905060e05260405160e051808203828111615a9357905090506101005260c05160801c61010051808202811583838304141715615a93579050905060a05160e051808202811583838304141715615a935790509050808201828110615a9357905090506040518015615a93578082049050905060a0526fffffffffffffffffffffffffffffffff60c0511661010051808202811583838304141715615a93579050905060805160e051808202811583838304141715615a935790509050808201828110615a9357905090506040518015615a9357808204905090506080525b60a0518152608051602082015250565b6040516020615b275f395f51808202811583838304141715615a9357905090508152670de0b6b3a76400006060516020615b475f395f51808202811583838304141715615a935790509050608051808202811583838304141715615a93579050905004602082015250565b60055460095411815250565b67ffffffffffffffff60405160801c16815267ffffffffffffffff60405160401c16602082015267ffffffffffffffff60405116604082015250565b6018546040526131416101006130f4565b61010060608160a05e50606051608051808201828110615a93579050905061010052606051673782dace9d900000810281673782dace9d900000820418615a93579050610100518015615a935780820490509050608051808202811583838304141715615a935790509050610100518015615a9357808204905090506101005260e05161010051808202811583838304141715615a935790509050670de0b6b3a764000060e05161010051808202811583838304141715615a93579050905004670de0b6b3a76400008101818110615a9357905061010051808203828111615a9357905090508015615a93578082049050905061010052670de0b6b3a764000060a05161010051808202811583838304141715615a93579050905060c0516101005180670de0b6b3a764000003670de0b6b3a76400008111615a93579050808202811583838304141715615a935790509050808201828110615a93579050905004815250565b600a546060526060516132bd575f8152506133db565b42600d54808203828111615a935790509050608052606051606051608051808202811583838304141715615a935790509050600c548015615a9357808204905090508082811882841002189050905060a0526040516133215760a0518152506133db565b5f60c052600e5460e0524260e05111156133945760e05142808203828111615a935790509050670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050600f548015615a935780820490509050670de0b6b3a7640000818118670de0b6b3a764000083100218905060c0525b60a05160c05180670de0b6b3a764000003670de0b6b3a76400008111615a93579050808202811583838304141715615a935790509050670de0b6b3a7640000810490508152505b565b604051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790508060011c9050606051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790508060b571010000000000000000000000000000000000821061344e578160801c91508060401b90505b6901000000000000000000821061346c578160401c91508060201b90505b650100000000008210613486578160201c91508060101b90505b6301000000821061349e578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c905080830480828118828410021890509050905090508015615a935780820490509050815250565b6003546101a0526004546101c0526002546101e05260175460405261353d6102606130f4565b6102606060816102005e506135536102806130e8565b6102805161026052600554610280525f6102a052426102805110156136a3575f546381d18d876102c05261024051610280514203670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050048060ff1c615a93577f80000000000000000000000000000000000000000000000000000000000000008114615a93575f036102e05260206102c060246102dc845afa6135f4573d5f5f3e3d5ffd5b60203d10615a93576102c09050516102a052670de0b6b3a76400006101c0516101e0518060011b818160011c18615a93579050808281188284100218905090506102a05180670de0b6b3a764000003670de0b6b3a76400008111615a93579050808202811583838304141715615a9357905090506101a0516102a051808202811583838304141715615a935790509050808201828110615a935790509050046101a0526101a051600355426005555b670de0b6b3a76400005f54637e0e395e6102c05260406101406102e05e610180516103205260406101006103405e60206102c060a46102dc845afa6136ea573d5f5f3e3d5ffd5b60203d10615a93576102c09050516101e051808202811583838304141715615a93579050905004600455601d546102c052600160405261372b6103006132a7565b610300516102e0526102c0516102e051808203828111615a9357905090506103005260165461032052610180516040526101e05160605261376d6103606133dd565b610360516103405261034051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790506102c0518015615a935780820490509050610360526103205161036051101561383a576102605161383a576020806103e0526017610380527f7669727475616c207072696365206465637265617365640000000000000000006103a052610380816103e001603782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06103c052806004016103dcfd5b60145461036051808201828110615a93579050905061032051808203828111615a935790509050610380526103805160145561038051670de0b6b3a76400008101818110615a935790508060011c905080670de0b6b3a7640000811882670de0b6b3a764000011021890506103a05261034051670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610300518015615a9357808204905090506103c052610360516103c0511015613966576020806104405260116103e0527f6e6567617469766520646f6e6174696f6e000000000000000000000000000000610400526103e08161044001603182825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610420528060040161043cfd5b6103a05161020051808201828110615a9357905090506103c0511161398b575f613992565b4261028051105b15613de0576101e051670de0b6b3a76400006101a05102046103e052670de0b6b3a76400016103e05110156139d8576103e051670de0b6b3a7640000036103e0526139eb565b670de0b6b3a76400006103e051036103e0525b6102205160056103e051048082811882841102189050905061040052610400516103e0511115613de0576103e0516101e051610400516103e05103808202811583838304141715615a935790509050610400516101a051808202811583838304141715615a935790509050808201828110615a935790509050046104205261014051610440526101e0516101605161042051808202811583838304141715615a93579050905004610460525f5463e68647666104a05260406101006104c05e60406104406105005e5f6105405260206104a060a46104bc845afa613ad1573d5f5f3e3d5ffd5b60203d10615a93576104a0905051610480526104805160405261042051606052613afc6104c06133dd565b6104c0516104a0526104a051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790506102c0518015615a9357808204905090506104c0525f6104e0526103a051610360518082811882841102189050905061050052610500516104c0511015613c8b576104a051670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610500518015615a935780820490509050610520526102c0516105205110613c23576020806105a052601a610540527f747765616b656420737570706c79206d75737420736872696e6b00000000000061056052610540816105a001603a82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610580528060040161059cfd5b610520516102c051036102e051808281188284100218905090506104e0526104a051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790506102c0516104e051808203828111615a9357905090508015615a9357808204905090506104c0525b670de0b6b3a76400016104c0511015613ca4575f613caf565b6103a0516104c05110155b15613de057610480516013556104c051601655610420516002556104e05115613dd4575f604052613ce16105406132a7565b61054051610520526102e05161054052610520516104e05161052051808202811583838304141715615a935790509050610540518015615a935780820490509050808203828111615a93579050905061056052600a546104e051808203828111615a935790509050610580525f6105a0526105805115613d6657610560511515613d68565b5f5b15613d9e5761056051600c54808202811583838304141715615a935790509050610580518015615a9357808204905090506105a0525b61058051600a55601d546104e051808203828111615a935790509050601d55426105a051808203828111615a935790509050600d555b61042051815250613df6565b61018051601355610360516016556101e0518152505b565b610620516106005118613e7d576020806106e0526009610680527f73616d6520636f696e00000000000000000000000000000000000000000000006106a052610680816106e001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06106c052806004016106dcfd5b61064051613efd576020806106e0526007610680527f7a65726f206478000000000000000000000000000000000000000000000000006106a052610680816106e001602782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06106c052806004016106dcfd5b613f086106c0612f24565b6106c06040816106805e506011546106c0526012546106e0525f61070052610620516002811015615a935760051b6106c0015161072052610600516002811015615a935760051b6106c0015161064051808203828111615a935790509050610740526002546107605260406106c060405e61076051608052613f8b6107c061307d565b6107c06040816107805e50613fa16107c06130e8565b6107c051156140ac57610740516020610600516002811015615a935760051b615b27015f395f51808202811583838304141715615a93579050905061074052610600511561401457670de0b6b3a76400006107405161076051808202811583838304141715615a93579050905004610740525b610600516002811015615a935760051b61078001516107e05261074051610600516002811015615a935760051b61078001525f5463e68647666108005260406106806108205e60406107806108605e5f6108a052602061080060a461081c845afa614081573d5f5f3e3d5ffd5b60203d10615a93576108009050516013556107e051610600516002811015615a935760051b61078001525b6013546107c0525f546343d188fb6108205260406106806108405e60406107806108805e6107c0516108c052610620516108e052604061082060c461083c845afa6140f9573d5f5f3e3d5ffd5b60403d10615a935761082090506040816107e05e50610620516002811015615a935760051b61078001516107e051808203828111615a93579050905061070052610620516002811015615a935760051b61078001805161070051808203828111615a9357905090508152506107005160018103818111615a935790506107005261062051156141ba5761070051670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610760518015615a935780820490509050610700525b610700516020610620516002811015615a935760051b615b27015f395f518015615a935780820490509050610700526402540be400604061078060605e614202610840613130565b6108405161070051808202811583838304141715615a93579050905004610820526107005161082051808203828111615a93579050905061070052610660516107005110156142c3576020806108a0526008610840527f736c69707061676500000000000000000000000000000000000000000000000061086052610840816108a001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610880528060040161089cfd5b6107205161070051808203828111615a93579050905061072052610720516020610620516002811015615a935760051b615b27015f395f51808202811583838304141715615a93579050905061072052610620511561434757670de0b6b3a76400006107205161076051808202811583838304141715615a93579050905004610720525b61072051610620516002811015615a935760051b61078001525f5463e68647666108405260406106806108605e60406107806108a05e610800516108e052602061084060a461085c845afa61439e573d5f5f3e3d5ffd5b60203d10615a93576108409050516107c05260406106806101005e60406107806101405e6107c051610180526143d5610840613517565b610840516107605261070051815261082051602082015261076051604082015250565b6040516002811015615a93576011018054606051808203828111615a93579050905081555060206040516002811015615a935760051b604001615b27015f395f5163a9059cbb60a05260805160c05260605160e052602060a0604460bc5f855af1614465573d5f5f3e3d5ffd5b3d61447c57803b15615a93576001610100526144a3565b3d602081183d60201002188060a00160c011615a935760a0518060011c615a935761010052505b61010090505161452557602080610180526009610120527f217472616e736665720000000000000000000000000000000000000000000000610140526101208161018001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610160528060040161017cfd5b565b61453160c06130e8565b60c0516145435760135481525061458a565b5f5463e686476660e052604060406101005e604060806101405e5f61018052602060e060a460fc845afa614579573d5f5f3e3d5ffd5b60203d10615a935760e09050518152505b565b6101e051156145a157620186a0815250614859565b60406101606102405e61022051156145bb57604036610240375b60115461024051808203828111615a9357905090506020615b275f395f51808202811583838304141715615a935790509050670de0b6b3a7640000810281670de0b6b3a7640000820418615a9357905060125461026051808203828111615a9357905090506020615b475f395f51808202811583838304141715615a9357905090508015615a93578082049050905061028052604061016060405e610280516080526146686102a061307d565b6102a06040816102e05e5060406102e06101605e60406101a060605e61468f6102c0613130565b6102c05160011b60021c6102a0525f6102c0525f6002905b8060051b61016001516102e0526102c0516102e051808201828110615a9357905090506102c0526001018181186146a75750506102c05160011c6102e0525f610300525f6002905b8060051b6101600151610320526102e051610320511161472d5761030051610320516102e05103808201828110615a9357905090506103005261474d565b610300516102e0516103205103808201828110615a935790509050610300525b6001018181186146ef5750505f61032052610200511561480757600e546103405242610340511115614807576103405142808203828111615a935790509050670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050600f548015615a935780820490509050670de0b6b3a7640000818118670de0b6b3a764000083100218905061036052610360516102a051808202811583838304141715615a935790509050670de0b6b3a764000081049050610320525b6102a05161030051808202811583838304141715615a9357905090506102c0518015615a935780820490509050620186a08101818110615a9357905061032051808201828110615a9357905090508152505b565b601d54606051808201828110615a935790509050601d55601b6040516020525f5260405f208054606051808201828110615a9357905090508155506040515f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60605160805260206080a36001815250565b601d54606051808203828111615a935790509050601d55601b6040516020525f5260405f208054606051808203828111615a9357905090508155505f6040517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60605160805260206080a36001815250565b601d54600a541415614aac576020615ba75f395f5163cab4d3db610200526020610200600461021c845afa614976573d5f5f3e3d5ffd5b3d602081183d6020100218806102000161022011615a9357610200518060a01c615a935761024052506102409050516101e0526101e051614a10576020615ba75f395f5163f851a440610200526020610200600461021c845afa6149dc573d5f5f3e3d5ffd5b3d602081183d6020100218806102000161022011615a9357610200518060a01c615a935761024052506102409050516101e0525b60115461020052601254610220525f6002905b806102405261024051604052610240516002811015615a935760051b61020001516060526101e051608052614a566143f8565b600101818118614a235750505f600a555f601d555f6013555f600e556101e0517fdd3c0336a16f1b64f172b7bb0dad5b2b3c7c76f91e8c4aafd6aae60dce80015360406102006102405e5f610280526060610240a25b565b601a546101e0526201517f6101e05142031115614ad957614ad06102006130e8565b61020051614adc565b60015b614e68576014546102005260155461022052601d546102405261022051610200511115614b1757670de0b6b3a763ffff610240511115614b1a565b60015b614e685760135461026052601654610280526002546102a0526020615ba75f395f5163cab4d3db6102e05260206102e060046102fc845afa614b5e573d5f5f3e3d5ffd5b3d602081183d6020100218806102e00161030011615a93576102e0518060a01c615a935761032052506103209050516102c0526011546102e052601254610300526404a817c800610220516102005103601954808202811583838304141715615a93579050905004610320525f610340526102c05115614be357610320511515614be5565b5f5b15614cb15761028051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790506102805161032051808203828111615a9357905090508015615a935780820490509050670de0b6b3a76400008103818111615a9357905061036052610340516102405161036051808202811583838304141715615a935790509050670de0b6b3a764000081049050808201828110615a9357905090506103405261020051610320518060011b818160011c18615a93579050808203828111615a935790509050610200525b6102405161034051808201828110615a93579050905061036052610260516040526102a051606052614ce46103806133dd565b61038051670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610360518015615a93578082049050905061028052670de0b6b3a763ffff610280511115614e68576102005160145542601a556102805160165561026051610360516102605161034051808202811583838304141715615a93579050905004808203828111615a93579050905060135561022051610200511115614d8c57610200516015555b604036610380376103405115614e68575f6002905b806103c0526103c0516002811015615a935760051b6102e0015161034051808202811583838304141715615a935790509050610360518015615a9357808204905090506103c0516002811015615a935760051b61038001526103c0516040526103c0516002811015615a935760051b61038001516060526102c051608052614e276143f8565b600101818118614da15750506102c0517f3bbd5f2f4711532d6e9ee88dfdf2f1468e9a4c3ae5e14d2e1a67bf4242d008d060406103806103c05e60406103c0a25b565b601d5461042052610420516103c0511115614ef7576020806104a0526007610440527f21616d6f756e740000000000000000000000000000000000000000000000000061046052610440816104a001602782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610480528060040161049cfd5b6103e0518060010360018111615a935790506104405260115461046052601254610480526002546104a052604061046060405e6104a051608052614f3c61050061307d565b6105006040816104c05e50604061038060405e60406104c060805e614f62610520614527565b6105205161050052610420516103c05161050051808202811583838304141715615a935790509050046105205260406104c06105405e6020615b275f395f51670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610580526104a0516020615b475f395f51808202811583838304141715615a9357905090506105a0526040366105c037670de0b6b3a7640000610400516103e0516002811015615a935760051b6105800151808202811583838304141715615a935790509050046103e0516002811015615a935760051b6105c001526103e0516002811015615a935760051b6105400180516103e0516002811015615a935760051b6105c00151808203828111615a9357905090508152505f546343d188fb6106205260406103806106405e60406105406106805e6105005161052051808203828111615a9357905090506106c052610440516106e052604061062060c461063c845afa6150cf573d5f5f3e3d5ffd5b60403d10615a935761062090505161060052610440516002811015615a935760051b6104c0015161060051808203828111615a935790509050610440516002811015615a935760051b6105c0015261060051610440516002811015615a935760051b610540015260403661062037610400516103e0516002811015615a935760051b61062001526103e0516151b0576105e051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790506020615b475f395f518015615a9357808204905090506104a0518015615a935780820490509050610640526151cf565b6105c0516020615b275f395f518015615a935780820490509050610620525b6106205161064051808201828110615a935790509050615261576020806106c0526007610660527f21746f6b656e730000000000000000000000000000000000000000000000000061068052610660816106c001602782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06106a052806004016106bcfd5b60406106206101605e60406105406101a05e6060366101e03761528561068061458c565b6106805161066052610520516105205161066051808202811583838304141715615a9357905090506402540be4008104905060018101818110615a93579050808203828111615a935790509050610520525f546343d188fb6106805260406103806106a05e60406105406106e05e6105005161052051808203828111615a935790509050610720526104405161074052604061068060c461069c845afa61532e573d5f5f3e3d5ffd5b60403d10615a935761068090505161060052610440516002811015615a935760051b6104c0015161060051808203828111615a935790509050670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610440516002811015615a935760051b61058001518015615a9357808204905090506106805261060051610440516002811015615a935760051b61054001526106805181526105005161052051808203828111615a9357905090506020820152604081016040610540825e5061066051608082015250565b615405614aae565b615410610840612f24565b6108406040816108005e5060a0366108403760406108006103805e60606107606103c05e61543f6108e0614e6a565b6108e060a0816109805e5060406109806108405e60406109c06108805e610a00516108c0526107c0516108405110156154ea576020806109405260086108e0527f736c697070616765000000000000000000000000000000000000000000000000610900526108e08161094001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610920528060040161093cfd5b33604052610760516060526155006108e06148cd565b6108e05060406108006101005e60406108806101405e6108605161018052615529610900613517565b610900516108e0526107a0511561555157604061078060405e6107e0516080526155516143f8565b610780518060010360018111615a935790506109005261090051604052610840516060526107e0516080526155846143f8565b604036610920376107a051610780516002811015615a935760051b610920015261084051610900516002811015615a935760051b61092001526107a05161564957337fe200e24d4a4c7cd367dd9befe394dc8a14e6d58c88ff5e2f512d65a9e0aa9c5c61076051610960526109005161098052610840516109a0526108c05161076051808202811583838304141715615a9357905090506402540be4008104905060018101818110615a935790506109c0526108e0516109e05260a0610960a26156c2565b337f22f9ea3e7d7b113cb423896d2e121f96a66c17814ac7f63d69096769fa3e2a55610760516109605260406109206109805e6108c05161076051808202811583838304141715615a9357905090506402540be4008104905060018101818110615a935790506109c0526108e0516109e05260a0610960a25b6156ca61493f565b61084051815250565b608051601c6040516020525f5260405f20806060516020525f5260405f209050556060516040517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560805160a052602060a0a3565b60605130811461573a5780151561573c565b5f5b90506157b75760208061012052600960c0527f217265636569766572000000000000000000000000000000000000000000000060e05260c08161012001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610100528060040161011cfd5b601b6040516020525f5260405f208054608051808203828111615a935790509050815550601b6060516020525f5260405f208054608051808201828110615a9357905090508155506060516040517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60805160a052602060a0a3565b60035460605260025460805260055460a0524260a05110156159a65760045460c0526017546040526158666101006130f4565b6101006040810190505160e0525f546381d18d876101205260a0514203670de0b6b3a7640000810281670de0b6b3a7640000820418615a9357905060e0518015615a9357808204905090508060ff1c615a93577f80000000000000000000000000000000000000000000000000000000000000008114615a93575f03610140526020610120602461013c845afa6158ff573d5f5f3e3d5ffd5b60203d10615a93576101209050516101005260c0516080518060011b818160011c18615a93579050808281188284100218905090506101005180670de0b6b3a764000003670de0b6b3a76400008111615a93579050808202811583838304141715615a93579050905060605161010051808202811583838304141715615a935790509050808201828110615a935790509050670de0b6b3a7640000810490508152506159ad565b6060518152505b565b6020615ba75f395f5163f851a440604052602060406004605c845afa6159d7573d5f5f3e3d5ffd5b3d602081183d602010021880604001606011615a93576040518060a01c615a9357608052506080905051331815615a7b5760208061010052600a60a0527f6f6e6c79206f776e65720000000000000000000000000000000000000000000060c05260a08161010001602a82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a060e0528060040160fcfd5b565b60805160605160401b60405160801b1717815250565b5f80fd2c4d2c122b3d2c4d2c4d2acd185e14fc00852c4d2c4d13200c7315db104d2861001810a41adf180c0373197c2a5c2b2103bd2c4d268401942c4d0f6211950cb813e72bad15a01a3b293b2ae918ef2bf717652c4d2c4d128f11682c4d2c4d2c4d2c4d2c4d2c4d2c4d2c4d03231a59189b21a319542b5913662b752c4d10f101b32c4d2b9119a8144c2c4d2a7725b22c4d855820bf0aeab4893a0fb1ddfeb10813812fb2348a0c919a25f46048ec7223d56f2c74195b27811890190140a1657679706572830004030039000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c5990000000000000000000000001fd8af16dc4bebd950521308d55d0543b6cdf4a1d6fe432991d14bd469ee0cce1efbd32851eb4eff43b257126e94d293caf48361000000000000000000000002540be4000000000000000000000000000000000100000000000000000000000000015f90000000000000000000038d7ea4c6800000000000000000000000000005f5e1000000000005f5e100000aa87bee53800000000000000000000000000005f5e100000000174876e80000000000000003620000000000000000000000000000000000000000000017edbe05b457450000000000000000000000000000000000000000000000000000000000000000000007594220574254430000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086379622d77627463000000000000000000000000000000000000000000000000

Deployed Bytecode

0x5f3560e01c60026048820660011b615a9701601e395f51565b635b41b908811861003757608436103417615a935733610900526100aa565b63d5f8da308118612c4d57606436103417615a93576020610059610760612f24565b6107606040816108405e50606060046108803760a06108406103805e6100806107a0614e6a565b6107a0f35b63a64833a081186101785760a436103417615a93576084358060a01c615a9357610900525b5f5c600114615a935760015f5d600435604052604435606052336080525f60a0526100d6610940612c51565b610940516109205260406004610600376109205161064052606435610660526101006109a0613df8565b6109a06060816109405e5060243560405261094051606052610900516080526101276143f8565b337f143f1f8e861fbdeddd5b46e844b7d3ac7b86a122f36e8c463859ee6811b1f29c6004356109a052610920516109c0526024356109e0526060610940610a005e60c06109a0a260206109405f5f5df35b633dd654788118612c4d5734615a935760175460405260206040f35b6329b244bb8118612c4d57608436103417615a935733610900526101d8565b63767691e781186102a75760a436103417615a93576084358060a01c615a9357610900525b5f5c600114615a935760015f5d60043560405260443560605233608052600160a052610205610940612c51565b6109405161092052604060046106003761092051610640526064356106605261022f6109a0613df8565b6109a06060816109405e5060243560405261094051606052610900516080526102566143f8565b337f143f1f8e861fbdeddd5b46e844b7d3ac7b86a122f36e8c463859ee6811b1f29c6004356109a052610920516109c0526024356109e0526060610940610a005e60c06109a0a260206109405f5f5df35b6348155d27811861030757608436103417615a93576064356040525b60206001546333e39968606052606060046080373060e052604051610100526020606060a4607c845afa6102f9573d5f5f3e3d5ffd5b60203d10615a935760609050f35b63f9ed95978118612c4d5734615a935760095460405260206040f35b630b4c7e4d811861034757606436103417615a935733610600525f610620526103f1565b63083812e58118612c4d5734615a9357602060175460405261036960606130f4565b6060602081019050f35b630c3e4b5481186103a157608436103417615a93576064358060a01c615a9357610600525f610620526103f1565b63c146bf948118612c4d5734615a935760045460405260206040f35b63865147388118612c4d5760a436103417615a93576064358060a01c615a9357610600526084358060011c615a9357610620525b5f5c600114615a935760015f5d600435602435808201828110615a93579050905061048e576020806106a0526008610640527f21616d6f756e747300000000000000000000000000000000000000000000000061066052610640816106a001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610680528060040161069cfd5b6011546106405260125461066052604036610680376011546106c0526012546106e0525f6002905b8061070052610700516002811015615a935760051b600401351561055b5761070051604052610700516002811015615a935760051b60040135606052336080525f60a052610505610720612c51565b61072051610700516002811015615a935760051b6106800152610700516002811015615a935760051b6106c0018051610700516002811015615a935760051b6106800151808201828110615a9357905090508152505b6001018181186104b65750506002546107005260406106c060405e6107005160805261058861076061307d565b6107606040816107205e50604061064060405e610700516080526105ad6107a061307d565b6107a06040816107605e506013546105c457426009555b6105cf6107e0612f24565b6107e06040816107a05e5060406107a060405e604061076060805e6105f5610800614527565b610800516107e0525f5463e68647666108205260406107a06108405e60406107206108805e5f6108c052602061082060a461083c845afa610638573d5f5f3e3d5ffd5b60203d10615a935761082090505161080052601d54610820525f610840526107e051156106a7576108205161080051808202811583838304141715615a9357905090506107e0518015615a93578082049050905061082051808203828111615a935790509050610840526106c9565b61080051604052610700516060526106c06108606133dd565b61086051610840525b61084051610749576020806108c052600e610860527f6e6f7468696e67206d696e74656400000000000000000000000000000000000061088052610860816108c001602e82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06108a052806004016108bcfd5b5f610860526107e05115610b3c5760406106806101605e60406107206101a05e610620516101e0526001610200525f6102205261078761088061458c565b6108805161084051808202811583838304141715615a9357905090506402540be4008104905060018101818110615a93579050610860526108405161086051808203828111615a93579050905061084052610620516109045761084051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790506108205161084051808201828110615a9357905090508015615a93578082049050905061088052610880511561083c57600a54151561083e565b5f5b156108e257600f546108a052610880516108a051808202811583838304141715615a9357905090506010548015615a9357808204905090506108a051808281188284100218905090506108c052600e5442808281188284110218905090506108e0526108e0516108c051808201828110615a935790509050426108a051808201828110615a935790509050808281188284100218905090506109005261090051600e555b61060051604052610840516060526108fb6108a061485b565b6108a050610b0a565b6106005115610985576020806108e0526010610880527f6e6f6e7a65726f207265636569766572000000000000000000000000000000006108a052610880816108e001603082825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06108c052806004016108dcfd5b600a5461084051808201828110615a93579050905061088052600b5461088051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790506108205161084051808201828110615a9357905090508015615a9357808204905090501115610a64576020806109005260136108a0527f646f6e6174696f6e2061626f76652063617021000000000000000000000000006108c0526108a08161090001603382825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06108e052806004016108fcfd5b5f604052610a736108c06132a7565b6108c051600c54808202811583838304141715615a935790509050610880518015615a9357808204905090506108a052426108a051808203828111615a935790509050600d5561088051600a55601d5461084051808201828110615a935790509050601d55337fc05458c16b884817a70d3d18223db5fe4adb4cb541a5573bef0daae7a6f2054260406106806108c05e60406108c0a25b60406107a06101005e60406107206101405e6108005161018052610b2f610880613517565b6108805161070052610b85565b61080051601355670de0b6b3a7640000601655670de0b6b3a7640000601455670de0b6b3a76400006015556106005160405261084051606052610b8061088061485b565b610880505b604435610840511015610c0a576020806108e0526008610880527f736c6970706167650000000000000000000000000000000000000000000000006108a052610880816108e001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06108c052806004016108dcfd5b61060051337f0e1f3c59f25a027e14a3f55c68245d22089c42b1dcd09f123a11d4af3c0d6f7260406106806108805e610860516108c0526108205161084051808201828110615a9357905090506108e052610700516109005260a0610880a360206108405f5f5df35b635b36389c8118610c9257606436103417615a9357336102a052610cdd565b6392526c0c8118612c4d5734615a93576020601854604052610cb460606130f4565b6060f35b633eb1719f8118610f4657608436103417615a93576064358060a01c615a93576102a0525b5f5c600114615a935760015f5d601d546102c05233604052600435606052610d066102e06148cd565b6102e0506040366102e037601354610320526102c05160043518610d65575f6002905b8061034052610340516002811015615a935760110154610340516002811015615a935760051b6102e00152600101818118610d29575050610e71565b5f6002905b8061034052610340516002811015615a935760110154600435808202811583838304141715615a9357905090506102c0518015615a935780820490509050610340516002811015615a935760051b6102e00152610340516002811015615a935760051b60240135610340516002811015615a935760051b6102e001511015610e64576020806103c0526008610360527f736c69707061676500000000000000000000000000000000000000000000000061038052610360816103c001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06103a052806004016103bcfd5b600101818118610d6a5750505b610320516102c05161032051600435808202811583838304141715615a93579050905004808203828111615a9357905090506013555f6002905b806103405261034051604052610340516002811015615a935760051b6102e001516060526102a051608052610ede6143f8565b600101818118610eab575050337fdd3c0336a16f1b64f172b7bb0dad5b2b3c7c76f91e8c4aafd6aae60dce80015360406102e06103405e6102c051600435808203828111615a935790509050610380526060610340a2610f3c61493f565b60406102e05f5f5df35b637c71109f8118612c4d5734615a9357600d5460405260206040f35b63512d63658118610f8157608436103417615a935733610a2052611072565b6323b872dd8118612c4d57606436103417615a93576004358060a01c615a9357610180526024358060a01c615a93576101a052601c610180516020525f5260405f2080336020525f5260405f209050546101c0527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101c0511461102a5761018051604052336060526101c051604435808203828111615a93579050905060805261102a6156d3565b604061018060405e604435608052611040615728565b60016101e05260206101e0f35b63b2f9173e8118612c4d5760a436103417615a93576084358060a01c615a9357610a20525b5f5c600114615a935760015f5d60206080600461076037610a20516107e05261109c610a406153fd565b610a405f5f5df35b63f1dc3cc981186110c357606436103417615a935733610a2052611116565b634903b0d18118612c4d57602436103417615a93576004356002811015615a93576011015460405260206040f35b630fbcee6e8118612c4d57608436103417615a93576064358060a01c615a9357610a20525b5f5c600114615a935760015f5d6020600435610760526024358060010360018111615a93579050610780525f6107a0526044356107c052610a20516107e052611160610a406153fd565b610a405f5f5df35b631a2430cc8118612c4d5734615a9357601d54600a54808203828111615a93579050905060405260206040f35b634fb08c5e81186111fc57604436103417615a935760206111b7610760612f24565b6107606040816108405e50600435610880526024358060010360018111615a935790506108a0525f6108c05260a06108406103805e6111f76107a0614e6a565b6107a0f35b63244c7c2e8118612c4d5734615a93576112146159af565b61121f6101a0612f24565b6101a06040816101605e506101605160801b6101a052610180516101a051176101a0526101a0516006556101a05160085542600755426009557f5f0e7fba3d100c9e19446e1c92fe436f0a9a22fe99669360e4fdd6d3de2fc28460406101606101c05e426102005260606101c0a1005b63a9059cbb81186112d957604436103417615a93576004358060a01c615a93576101805233604052610180516060526024356080526112cc615728565b60016101a05260206101a0f35b6386fc88d381186113045734615a93575f5c600114615a935760206112ff610160615833565b610160f35b630b7b594b8118612c4d5734615a935760155460405260206040f35b63095ea7b38118612c4d57604436103417615a93576004358060a01c615a935760c0523360405260c05160605260243560805261135b6156d3565b600160e052602060e0f35b63cab4d3db81186113cb5734615a935760206020615ba75f395f5163cab4d3db604052602060406004605c845afa6113a0573d5f5f3e3d5ffd5b3d602081183d602010021880604001606011615a93576040518060a01c615a93576080525060809050f35b632a3f192b8118612c4d5734615a935760015460405260206040f35b63f851a4408118612c4d5734615a935760206020615ba75f395f5163f851a440604052602060406004605c845afa611421573d5f5f3e3d5ffd5b3d602081183d602010021880604001606011615a93576040518060a01c615a93576080525060809050f35b63ed8e84f381186114b257606436103417615a93576044358060011c615a9357604052602060015463bc5bc6b76060526040600460803760405160c0523060e052602060606084607c845afa6114a4573d5f5f3e3d5ffd5b60203d10615a935760609050f35b6306fdde038118612c4d5734615a9357602080604052806040016060615bc782398051806020830101601f825f03163682375050601f19601f825160200101169050810190506040f35b63556d6e9f811861154e57606436103417615a93576020600154633bb1f8c1604052606060046060373060c052602060406084605c845afa611540573d5f5f3e3d5ffd5b60203d10615a935760409050f35b63c6610657811861158457602436103417615a935760206004356002811015615a935760051b604001615b270160403960206040f35b63e89876ff8118612c4d5734615a935760075460405260206040f35b6337ed3a7a81186115bf57606436103417615a935760056040526102c3565b630c46b72a8118612c4d5734615a935760165460405260206040f35b6354f0f7d581186117405734615a93575f5c600114615a93576016548060011b818160011c18615a93579050611612610160615833565b61016051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790508060b571010000000000000000000000000000000000821061165d578160801c91508060401b90505b6901000000000000000000821061167b578160401c91508060201b90505b650100000000008210611695578160201c91508060101b90505b630100000082106116ad578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c90508083048082811882841002189050905090509050808202811583838304141715615a935790509050670de0b6b3a764000081049050610180526020610180f35b63b9e8c9fd8118612c4d5734615a93575f5c600114615a935760025460405260206040f35b63bb7b8b8081186117ce5734615a93575f5c600114615a935760135460405260025460605261179460806133dd565b608051670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050601d548015615a93578082049050905060a052602060a0f35b63f446c1d081186117f05734615a935760206117eb610120612f24565b610120f35b634d23bfa08118612c4d5734615a935760055460405260206040f35b63ddca3f438118612c4d5734615a9357602060115460405260125460605260025460805261183b61016061307d565b6101606040816101c05e5060406101c060605e6118596101a0613130565b6101a0f35b63bcc8342e811861187f57608436103417615a935760403661038037611923565b63fe192e9e8118612c4d5734615a935760105460405260206040f35b63326cc61781186118c95760a436103417615a93576084358060011c615a9357610380525f6103a052611923565b6349fe9e778118612c4d5734615a935760206017546040526118eb60606130f4565b6060f35b6357832be68118612c4d5760c436103417615a93576084358060011c615a93576103805260a4358060011c615a93576103a0525b60206040600461016037604060446101a03760406103806101e05e60016102205261194f6103c061458c565b6103c0f35b63b13739298118612c4d5734615a93576020611971610120612f24565b610120602081019050f35b63ee8de6758118612c4d5734615a9357602060185460405261199e60606130f4565b6060602081019050f35b6372d4f0e281186119d45734615a935760206018546040526119ca60606130f4565b6060604081019050f35b6309c3da6a8118611a1f5734615a93576017546040526119f460606130f4565b6060604081019050516102b68102816102b6820418615a935790506103e88104905060c052602060c0f35b630f529ba28118612c4d5734615a935760135460405260206040f35b633620604b8118612c4d5734615a93576040615b2760403960406040f35b6380823d9e8118611a8757604436103417615a9357602060406004606037611a82610160613130565b610160f35b63dd62ed3e8118612c4d57604436103417615a93576004358060a01c615a93576040526024358060a01c615a9357606052601c6040516020525f5260405f20806060516020525f5260405f2090505460805260206080f35b635e248072811861218757606436103417615a9357611afc6159af565b611b076101606130e8565b6101605115611b88576020806101e0526005610180527f2172616d700000000000000000000000000000000000000000000000000000006101a052610180816101e001602582825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101c052806004016101dcfd5b42620151808101818110615a9357905060018103818111615a9357905060443511611c25576020806101c052600d610160527f72616d702074696d653c6d696e0000000000000000000000000000000000000061018052610160816101c001602d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101a052806004016101bcfd5b611c306101a0612f24565b6101a06040816101605e506101605160801b6101a052610180516101a051176101a052614e206004351015611cd7576020806102205260056101c0527f413c6d696e0000000000000000000000000000000000000000000000000000006101e0526101c08161022001602582825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610200528060040161021cfd5b6305f5e1006004351115611d5d576020806102205260056101c0527f413e6d61780000000000000000000000000000000000000000000000000000006101e0526101c08161022001602582825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610200528060040161021cfd5b6402540be4006024351015611de4576020806102205260096101c0527f67616d6d613c6d696e00000000000000000000000000000000000000000000006101e0526101c08161022001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610200528060040161021cfd5b6702c2fd72164d80006024351115611e6e576020806102205260096101c0527f67616d6d653e6d617800000000000000000000000000000000000000000000006101e0526101c08161022001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610200528060040161021cfd5b600435670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610160518015615a9357808204905090506101c052678ac7230489e800006101c0511115611f2f5760208061024052600a6101e0527f4120746f6f206869676800000000000000000000000000000000000000000000610200526101e08161024001602a82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610220528060040161023cfd5b67016345785d8a00006101c0511015611fba576020806102405260096101e0527f4120746f6f206c6f770000000000000000000000000000000000000000000000610200526101e08161024001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610220528060040161023cfd5b602435670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610180518015615a9357808204905090506101c052678ac7230489e800006101c051111561207b5760208061024052600e6101e0527f67616d6d6120746f6f2068696768000000000000000000000000000000000000610200526101e08161024001602e82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610220528060040161023cfd5b67016345785d8a00006101c05110156121065760208061024052600d6101e0527f67616d6d6120746f6f206c6f7700000000000000000000000000000000000000610200526101e08161024001602d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610220528060040161023cfd5b6101a0516006554260075560043560801b6101e0526024356101e051176101e0526044356009556101e0516008557fe35f0559b0642164e286b30df2077ec3a05426617a25db7578fd20ba39a6cd0561016051610200526004356102205261018051610240526024356102605242610280526044356102a05260c0610200a1005b63d046b4ca8118612c4d5734615a9357600e5460405260206040f35b636dbcf350811861253d5760c436103417615a93575f5c600114615a935760015f5d6121cd6159af565b60606004610160376018546040526121e66102206130f4565b6102206060816101c05e506402540be40061018051111561220e576101e05161018052612294565b6207a12061018051101561229457602080610280526004610220527f2166656500000000000000000000000000000000000000000000000000000000610240526102208161028001602482825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610260528060040161027cfd5b6402540be40161016051106122ac576101c051610160525b6101805161016051111561233257602080610280526008610220527f216d69642d666565000000000000000000000000000000000000000000000000610240526102208161028001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610260528060040161027cfd5b670de0b6b3a763ffff6101a051111561235257610200516101a0526123d2565b6101a0516123d25760208061028052600a610220527f216665655f67616d6d6100000000000000000000000000000000000000000000610240526102208161028001602a82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610260528060040161027cfd5b606061016060405e6123e5610220615a7d565b6102205160185560606064610220376017546040526124056102e06130f4565b6102e06060816102805e50670de0b6b3a7640001610220511061242b5761028051610220525b670de0b6b3a76400016102405110612446576102a051610240525b620d505d610260511115612461576102c051610260526124e5565b60576102605110156124e55760208061034052600b6102e0527f4d413c36302f6c6e283229000000000000000000000000000000000000000000610300526102e08161034001602b82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610320528060040161033cfd5b606061022060405e6124f86102e0615a7d565b6102e0516017557fa32137411fc7c20db359079cd84af0e2cad58cd7a182a8a5e23e08e554e88bf060606101606102e05e60606102206103405e60c06102e0a15f5f5d005b6354fd4d508118612c4d5734615a935760208060805260076040527f76322e312e306400000000000000000000000000000000000000000000000000606052604081608001602782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506080f35b63f5b2f0168118612c4d57602436103417615a93576125cf6159af565b60043561264e576020806101c0526009610160527f216475726174696f6e000000000000000000000000000000000000000000000061018052610160816101c001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101a052806004016101bcfd5b600435600c557fb2cf7972e8e7c2db8a62b4c568cf133a24bf5910b2603ad8811e6bfc9a865322600435610160526020610160a1005b638325c0028118612c4d57606436103417615a93576126a16159af565b600435612720576020806101c0526007610160527f21706572696f640000000000000000000000000000000000000000000000000061018052610160816101c001602782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101a052806004016101bcfd5b60243561279f576020806101c052600a610160527f217468726573686f6c640000000000000000000000000000000000000000000061018052610160816101c001602a82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101a052806004016101bcfd5b60443561281e576020806101c052600b610160527f216d61785f73686172657300000000000000000000000000000000000000000061018052610160816101c001602b82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101a052806004016101bcfd5b600435600f55602435601055604435600b557f6db4ade9cd8d6e671d6d713ab38d8889f9e3d4bbb319ca3389a516cf4efcf19d60606004610160376060610160a1005b633217902f8118612c4d57602436103417615a935761287e6159af565b6402540be4006004351115612905576020806101c052600d610160527f61646d696e5f6665653e4d41580000000000000000000000000000000000000061018052610160816101c001602d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101a052806004016101bcfd5b6004356019557f2f0d0ace1d699b471d7b39522b5c8aae053bce1b422b7a4fe8f09bd6562a4b74600435610160526020610160a1005b636fe26a348118612c4d57604436103417615a93576004358060a01c615a9357610160526024358060a01c615a9357610180526129766159af565b610160511561298657600161298d565b6101805115155b612a09576020806102005260096101a0527f21636f6e747261637400000000000000000000000000000000000000000000006101c0526101a08161020001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101e052806004016101fcfd5b6101605115612a1a57610160516001555b6101805115612a2a57610180515f555b7f567da2faedc99f951794c7dc379b4906dcdd1d016fedb16916d1e9623901f21660406101606101a05e60406101a0a1005b63ed6c15468118612c4d5734615a93575f5460405260206040f35b63c45a01558118612a955734615a93576020615ba760403960206040f35b637ba1a74d8118612ab15734615a935760145460405260206040f35b63e36164058118612c4d5734615a935760185460405260206040f35b63204fe3d58118612c4d5734615a935760065460405260206040f35b63f30cfad58118612b055734615a935760085460405260206040f35b6318160ddd8118612c4d5734615a9357601d5460405260206040f35b63a3bdf1b78118612c4d5734615a9357600a5460405260206040f35b633d2699f28118612c4d5734615a9357600b5460405260206040f35b630decf4a28118612c4d5734615a9357600c5460405260206040f35b631f88619c8118612c4d5734615a9357600f5460405260206040f35b63fee3f7f98118612c4d5734615a935760195460405260206040f35b6395d89b418118612c4d5734615a9357602080604052806040016040615c2782398051806020830101601f825f03163682375050601f19601f825160200101169050810190506040f35b63313ce5678118612c4d5734615a9357601260405260206040f35b6370a082318118612c4d57602436103417615a93576004358060a01c615a9357604052601b6040516020525f5260405f205460605260206060f35b5f5ffd5b60206040516002811015615a935760051b604001615b27015f395f516370a0823160e0523061010052602060e0602460fc845afa612c91573d5f5f3e3d5ffd5b60203d10615a935760e090505160c05260a05115612d815760c0516040516002811015615a935760110154808203828111615a93579050905060e05260605160e0511015612d5157602080610160526006610100527f21636f696e730000000000000000000000000000000000000000000000000000610120526101008161016001602682825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610140528060040161015cfd5b6040516002811015615a9357601101805460e051808201828110615a93579050905081555060e051815250612f22565b60206040516002811015615a935760051b604001615b27015f395f516323b872dd60e05260805161010052306101205260605161014052602060e0606460fc5f855af1612dd0573d5f5f3e3d5ffd5b3d612de757803b15615a9357600161016052612e0f565b3d602081183d60201002188060e00161010011615a935760e0518060011c615a935761016052505b610160905051612e91576020806101e052600d610180527f217472616e7366657246726f6d000000000000000000000000000000000000006101a052610180816101e001602d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101c052806004016101dcfd5b60206040516002811015615a935760051b604001615b27015f395f516370a082316101005230610120526020610100602461011c845afa612ed4573d5f5f3e3d5ffd5b60203d10615a935761010090505160c051808203828111615a93579050905060e0526040516002811015615a9357601101805460e051808201828110615a93579050905081555060e0518152505b565b6009546040526008546060526fffffffffffffffffffffffffffffffff6060511660805260605160801c60a05260405142101561306d5760065460c05260075460e05260405160e051808203828111615a9357905090506040524260e051808203828111615a93579050905060e05260405160e051808203828111615a9357905090506101005260c05160801c61010051808202811583838304141715615a93579050905060a05160e051808202811583838304141715615a935790509050808201828110615a9357905090506040518015615a93578082049050905060a0526fffffffffffffffffffffffffffffffff60c0511661010051808202811583838304141715615a93579050905060805160e051808202811583838304141715615a935790509050808201828110615a9357905090506040518015615a9357808204905090506080525b60a0518152608051602082015250565b6040516020615b275f395f51808202811583838304141715615a9357905090508152670de0b6b3a76400006060516020615b475f395f51808202811583838304141715615a935790509050608051808202811583838304141715615a93579050905004602082015250565b60055460095411815250565b67ffffffffffffffff60405160801c16815267ffffffffffffffff60405160401c16602082015267ffffffffffffffff60405116604082015250565b6018546040526131416101006130f4565b61010060608160a05e50606051608051808201828110615a93579050905061010052606051673782dace9d900000810281673782dace9d900000820418615a93579050610100518015615a935780820490509050608051808202811583838304141715615a935790509050610100518015615a9357808204905090506101005260e05161010051808202811583838304141715615a935790509050670de0b6b3a764000060e05161010051808202811583838304141715615a93579050905004670de0b6b3a76400008101818110615a9357905061010051808203828111615a9357905090508015615a93578082049050905061010052670de0b6b3a764000060a05161010051808202811583838304141715615a93579050905060c0516101005180670de0b6b3a764000003670de0b6b3a76400008111615a93579050808202811583838304141715615a935790509050808201828110615a93579050905004815250565b600a546060526060516132bd575f8152506133db565b42600d54808203828111615a935790509050608052606051606051608051808202811583838304141715615a935790509050600c548015615a9357808204905090508082811882841002189050905060a0526040516133215760a0518152506133db565b5f60c052600e5460e0524260e05111156133945760e05142808203828111615a935790509050670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050600f548015615a935780820490509050670de0b6b3a7640000818118670de0b6b3a764000083100218905060c0525b60a05160c05180670de0b6b3a764000003670de0b6b3a76400008111615a93579050808202811583838304141715615a935790509050670de0b6b3a7640000810490508152505b565b604051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790508060011c9050606051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790508060b571010000000000000000000000000000000000821061344e578160801c91508060401b90505b6901000000000000000000821061346c578160401c91508060201b90505b650100000000008210613486578160201c91508060101b90505b6301000000821061349e578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c905080830480828118828410021890509050905090508015615a935780820490509050815250565b6003546101a0526004546101c0526002546101e05260175460405261353d6102606130f4565b6102606060816102005e506135536102806130e8565b6102805161026052600554610280525f6102a052426102805110156136a3575f546381d18d876102c05261024051610280514203670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050048060ff1c615a93577f80000000000000000000000000000000000000000000000000000000000000008114615a93575f036102e05260206102c060246102dc845afa6135f4573d5f5f3e3d5ffd5b60203d10615a93576102c09050516102a052670de0b6b3a76400006101c0516101e0518060011b818160011c18615a93579050808281188284100218905090506102a05180670de0b6b3a764000003670de0b6b3a76400008111615a93579050808202811583838304141715615a9357905090506101a0516102a051808202811583838304141715615a935790509050808201828110615a935790509050046101a0526101a051600355426005555b670de0b6b3a76400005f54637e0e395e6102c05260406101406102e05e610180516103205260406101006103405e60206102c060a46102dc845afa6136ea573d5f5f3e3d5ffd5b60203d10615a93576102c09050516101e051808202811583838304141715615a93579050905004600455601d546102c052600160405261372b6103006132a7565b610300516102e0526102c0516102e051808203828111615a9357905090506103005260165461032052610180516040526101e05160605261376d6103606133dd565b610360516103405261034051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790506102c0518015615a935780820490509050610360526103205161036051101561383a576102605161383a576020806103e0526017610380527f7669727475616c207072696365206465637265617365640000000000000000006103a052610380816103e001603782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06103c052806004016103dcfd5b60145461036051808201828110615a93579050905061032051808203828111615a935790509050610380526103805160145561038051670de0b6b3a76400008101818110615a935790508060011c905080670de0b6b3a7640000811882670de0b6b3a764000011021890506103a05261034051670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610300518015615a9357808204905090506103c052610360516103c0511015613966576020806104405260116103e0527f6e6567617469766520646f6e6174696f6e000000000000000000000000000000610400526103e08161044001603182825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610420528060040161043cfd5b6103a05161020051808201828110615a9357905090506103c0511161398b575f613992565b4261028051105b15613de0576101e051670de0b6b3a76400006101a05102046103e052670de0b6b3a76400016103e05110156139d8576103e051670de0b6b3a7640000036103e0526139eb565b670de0b6b3a76400006103e051036103e0525b6102205160056103e051048082811882841102189050905061040052610400516103e0511115613de0576103e0516101e051610400516103e05103808202811583838304141715615a935790509050610400516101a051808202811583838304141715615a935790509050808201828110615a935790509050046104205261014051610440526101e0516101605161042051808202811583838304141715615a93579050905004610460525f5463e68647666104a05260406101006104c05e60406104406105005e5f6105405260206104a060a46104bc845afa613ad1573d5f5f3e3d5ffd5b60203d10615a93576104a0905051610480526104805160405261042051606052613afc6104c06133dd565b6104c0516104a0526104a051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790506102c0518015615a9357808204905090506104c0525f6104e0526103a051610360518082811882841102189050905061050052610500516104c0511015613c8b576104a051670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610500518015615a935780820490509050610520526102c0516105205110613c23576020806105a052601a610540527f747765616b656420737570706c79206d75737420736872696e6b00000000000061056052610540816105a001603a82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610580528060040161059cfd5b610520516102c051036102e051808281188284100218905090506104e0526104a051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790506102c0516104e051808203828111615a9357905090508015615a9357808204905090506104c0525b670de0b6b3a76400016104c0511015613ca4575f613caf565b6103a0516104c05110155b15613de057610480516013556104c051601655610420516002556104e05115613dd4575f604052613ce16105406132a7565b61054051610520526102e05161054052610520516104e05161052051808202811583838304141715615a935790509050610540518015615a935780820490509050808203828111615a93579050905061056052600a546104e051808203828111615a935790509050610580525f6105a0526105805115613d6657610560511515613d68565b5f5b15613d9e5761056051600c54808202811583838304141715615a935790509050610580518015615a9357808204905090506105a0525b61058051600a55601d546104e051808203828111615a935790509050601d55426105a051808203828111615a935790509050600d555b61042051815250613df6565b61018051601355610360516016556101e0518152505b565b610620516106005118613e7d576020806106e0526009610680527f73616d6520636f696e00000000000000000000000000000000000000000000006106a052610680816106e001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06106c052806004016106dcfd5b61064051613efd576020806106e0526007610680527f7a65726f206478000000000000000000000000000000000000000000000000006106a052610680816106e001602782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06106c052806004016106dcfd5b613f086106c0612f24565b6106c06040816106805e506011546106c0526012546106e0525f61070052610620516002811015615a935760051b6106c0015161072052610600516002811015615a935760051b6106c0015161064051808203828111615a935790509050610740526002546107605260406106c060405e61076051608052613f8b6107c061307d565b6107c06040816107805e50613fa16107c06130e8565b6107c051156140ac57610740516020610600516002811015615a935760051b615b27015f395f51808202811583838304141715615a93579050905061074052610600511561401457670de0b6b3a76400006107405161076051808202811583838304141715615a93579050905004610740525b610600516002811015615a935760051b61078001516107e05261074051610600516002811015615a935760051b61078001525f5463e68647666108005260406106806108205e60406107806108605e5f6108a052602061080060a461081c845afa614081573d5f5f3e3d5ffd5b60203d10615a93576108009050516013556107e051610600516002811015615a935760051b61078001525b6013546107c0525f546343d188fb6108205260406106806108405e60406107806108805e6107c0516108c052610620516108e052604061082060c461083c845afa6140f9573d5f5f3e3d5ffd5b60403d10615a935761082090506040816107e05e50610620516002811015615a935760051b61078001516107e051808203828111615a93579050905061070052610620516002811015615a935760051b61078001805161070051808203828111615a9357905090508152506107005160018103818111615a935790506107005261062051156141ba5761070051670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610760518015615a935780820490509050610700525b610700516020610620516002811015615a935760051b615b27015f395f518015615a935780820490509050610700526402540be400604061078060605e614202610840613130565b6108405161070051808202811583838304141715615a93579050905004610820526107005161082051808203828111615a93579050905061070052610660516107005110156142c3576020806108a0526008610840527f736c69707061676500000000000000000000000000000000000000000000000061086052610840816108a001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610880528060040161089cfd5b6107205161070051808203828111615a93579050905061072052610720516020610620516002811015615a935760051b615b27015f395f51808202811583838304141715615a93579050905061072052610620511561434757670de0b6b3a76400006107205161076051808202811583838304141715615a93579050905004610720525b61072051610620516002811015615a935760051b61078001525f5463e68647666108405260406106806108605e60406107806108a05e610800516108e052602061084060a461085c845afa61439e573d5f5f3e3d5ffd5b60203d10615a93576108409050516107c05260406106806101005e60406107806101405e6107c051610180526143d5610840613517565b610840516107605261070051815261082051602082015261076051604082015250565b6040516002811015615a93576011018054606051808203828111615a93579050905081555060206040516002811015615a935760051b604001615b27015f395f5163a9059cbb60a05260805160c05260605160e052602060a0604460bc5f855af1614465573d5f5f3e3d5ffd5b3d61447c57803b15615a93576001610100526144a3565b3d602081183d60201002188060a00160c011615a935760a0518060011c615a935761010052505b61010090505161452557602080610180526009610120527f217472616e736665720000000000000000000000000000000000000000000000610140526101208161018001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610160528060040161017cfd5b565b61453160c06130e8565b60c0516145435760135481525061458a565b5f5463e686476660e052604060406101005e604060806101405e5f61018052602060e060a460fc845afa614579573d5f5f3e3d5ffd5b60203d10615a935760e09050518152505b565b6101e051156145a157620186a0815250614859565b60406101606102405e61022051156145bb57604036610240375b60115461024051808203828111615a9357905090506020615b275f395f51808202811583838304141715615a935790509050670de0b6b3a7640000810281670de0b6b3a7640000820418615a9357905060125461026051808203828111615a9357905090506020615b475f395f51808202811583838304141715615a9357905090508015615a93578082049050905061028052604061016060405e610280516080526146686102a061307d565b6102a06040816102e05e5060406102e06101605e60406101a060605e61468f6102c0613130565b6102c05160011b60021c6102a0525f6102c0525f6002905b8060051b61016001516102e0526102c0516102e051808201828110615a9357905090506102c0526001018181186146a75750506102c05160011c6102e0525f610300525f6002905b8060051b6101600151610320526102e051610320511161472d5761030051610320516102e05103808201828110615a9357905090506103005261474d565b610300516102e0516103205103808201828110615a935790509050610300525b6001018181186146ef5750505f61032052610200511561480757600e546103405242610340511115614807576103405142808203828111615a935790509050670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050600f548015615a935780820490509050670de0b6b3a7640000818118670de0b6b3a764000083100218905061036052610360516102a051808202811583838304141715615a935790509050670de0b6b3a764000081049050610320525b6102a05161030051808202811583838304141715615a9357905090506102c0518015615a935780820490509050620186a08101818110615a9357905061032051808201828110615a9357905090508152505b565b601d54606051808201828110615a935790509050601d55601b6040516020525f5260405f208054606051808201828110615a9357905090508155506040515f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60605160805260206080a36001815250565b601d54606051808203828111615a935790509050601d55601b6040516020525f5260405f208054606051808203828111615a9357905090508155505f6040517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60605160805260206080a36001815250565b601d54600a541415614aac576020615ba75f395f5163cab4d3db610200526020610200600461021c845afa614976573d5f5f3e3d5ffd5b3d602081183d6020100218806102000161022011615a9357610200518060a01c615a935761024052506102409050516101e0526101e051614a10576020615ba75f395f5163f851a440610200526020610200600461021c845afa6149dc573d5f5f3e3d5ffd5b3d602081183d6020100218806102000161022011615a9357610200518060a01c615a935761024052506102409050516101e0525b60115461020052601254610220525f6002905b806102405261024051604052610240516002811015615a935760051b61020001516060526101e051608052614a566143f8565b600101818118614a235750505f600a555f601d555f6013555f600e556101e0517fdd3c0336a16f1b64f172b7bb0dad5b2b3c7c76f91e8c4aafd6aae60dce80015360406102006102405e5f610280526060610240a25b565b601a546101e0526201517f6101e05142031115614ad957614ad06102006130e8565b61020051614adc565b60015b614e68576014546102005260155461022052601d546102405261022051610200511115614b1757670de0b6b3a763ffff610240511115614b1a565b60015b614e685760135461026052601654610280526002546102a0526020615ba75f395f5163cab4d3db6102e05260206102e060046102fc845afa614b5e573d5f5f3e3d5ffd5b3d602081183d6020100218806102e00161030011615a93576102e0518060a01c615a935761032052506103209050516102c0526011546102e052601254610300526404a817c800610220516102005103601954808202811583838304141715615a93579050905004610320525f610340526102c05115614be357610320511515614be5565b5f5b15614cb15761028051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790506102805161032051808203828111615a9357905090508015615a935780820490509050670de0b6b3a76400008103818111615a9357905061036052610340516102405161036051808202811583838304141715615a935790509050670de0b6b3a764000081049050808201828110615a9357905090506103405261020051610320518060011b818160011c18615a93579050808203828111615a935790509050610200525b6102405161034051808201828110615a93579050905061036052610260516040526102a051606052614ce46103806133dd565b61038051670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610360518015615a93578082049050905061028052670de0b6b3a763ffff610280511115614e68576102005160145542601a556102805160165561026051610360516102605161034051808202811583838304141715615a93579050905004808203828111615a93579050905060135561022051610200511115614d8c57610200516015555b604036610380376103405115614e68575f6002905b806103c0526103c0516002811015615a935760051b6102e0015161034051808202811583838304141715615a935790509050610360518015615a9357808204905090506103c0516002811015615a935760051b61038001526103c0516040526103c0516002811015615a935760051b61038001516060526102c051608052614e276143f8565b600101818118614da15750506102c0517f3bbd5f2f4711532d6e9ee88dfdf2f1468e9a4c3ae5e14d2e1a67bf4242d008d060406103806103c05e60406103c0a25b565b601d5461042052610420516103c0511115614ef7576020806104a0526007610440527f21616d6f756e740000000000000000000000000000000000000000000000000061046052610440816104a001602782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610480528060040161049cfd5b6103e0518060010360018111615a935790506104405260115461046052601254610480526002546104a052604061046060405e6104a051608052614f3c61050061307d565b6105006040816104c05e50604061038060405e60406104c060805e614f62610520614527565b6105205161050052610420516103c05161050051808202811583838304141715615a935790509050046105205260406104c06105405e6020615b275f395f51670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610580526104a0516020615b475f395f51808202811583838304141715615a9357905090506105a0526040366105c037670de0b6b3a7640000610400516103e0516002811015615a935760051b6105800151808202811583838304141715615a935790509050046103e0516002811015615a935760051b6105c001526103e0516002811015615a935760051b6105400180516103e0516002811015615a935760051b6105c00151808203828111615a9357905090508152505f546343d188fb6106205260406103806106405e60406105406106805e6105005161052051808203828111615a9357905090506106c052610440516106e052604061062060c461063c845afa6150cf573d5f5f3e3d5ffd5b60403d10615a935761062090505161060052610440516002811015615a935760051b6104c0015161060051808203828111615a935790509050610440516002811015615a935760051b6105c0015261060051610440516002811015615a935760051b610540015260403661062037610400516103e0516002811015615a935760051b61062001526103e0516151b0576105e051670de0b6b3a7640000810281670de0b6b3a7640000820418615a935790506020615b475f395f518015615a9357808204905090506104a0518015615a935780820490509050610640526151cf565b6105c0516020615b275f395f518015615a935780820490509050610620525b6106205161064051808201828110615a935790509050615261576020806106c0526007610660527f21746f6b656e730000000000000000000000000000000000000000000000000061068052610660816106c001602782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06106a052806004016106bcfd5b60406106206101605e60406105406101a05e6060366101e03761528561068061458c565b6106805161066052610520516105205161066051808202811583838304141715615a9357905090506402540be4008104905060018101818110615a93579050808203828111615a935790509050610520525f546343d188fb6106805260406103806106a05e60406105406106e05e6105005161052051808203828111615a935790509050610720526104405161074052604061068060c461069c845afa61532e573d5f5f3e3d5ffd5b60403d10615a935761068090505161060052610440516002811015615a935760051b6104c0015161060051808203828111615a935790509050670de0b6b3a7640000810281670de0b6b3a7640000820418615a93579050610440516002811015615a935760051b61058001518015615a9357808204905090506106805261060051610440516002811015615a935760051b61054001526106805181526105005161052051808203828111615a9357905090506020820152604081016040610540825e5061066051608082015250565b615405614aae565b615410610840612f24565b6108406040816108005e5060a0366108403760406108006103805e60606107606103c05e61543f6108e0614e6a565b6108e060a0816109805e5060406109806108405e60406109c06108805e610a00516108c0526107c0516108405110156154ea576020806109405260086108e0527f736c697070616765000000000000000000000000000000000000000000000000610900526108e08161094001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610920528060040161093cfd5b33604052610760516060526155006108e06148cd565b6108e05060406108006101005e60406108806101405e6108605161018052615529610900613517565b610900516108e0526107a0511561555157604061078060405e6107e0516080526155516143f8565b610780518060010360018111615a935790506109005261090051604052610840516060526107e0516080526155846143f8565b604036610920376107a051610780516002811015615a935760051b610920015261084051610900516002811015615a935760051b61092001526107a05161564957337fe200e24d4a4c7cd367dd9befe394dc8a14e6d58c88ff5e2f512d65a9e0aa9c5c61076051610960526109005161098052610840516109a0526108c05161076051808202811583838304141715615a9357905090506402540be4008104905060018101818110615a935790506109c0526108e0516109e05260a0610960a26156c2565b337f22f9ea3e7d7b113cb423896d2e121f96a66c17814ac7f63d69096769fa3e2a55610760516109605260406109206109805e6108c05161076051808202811583838304141715615a9357905090506402540be4008104905060018101818110615a935790506109c0526108e0516109e05260a0610960a25b6156ca61493f565b61084051815250565b608051601c6040516020525f5260405f20806060516020525f5260405f209050556060516040517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560805160a052602060a0a3565b60605130811461573a5780151561573c565b5f5b90506157b75760208061012052600960c0527f217265636569766572000000000000000000000000000000000000000000000060e05260c08161012001602982825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610100528060040161011cfd5b601b6040516020525f5260405f208054608051808203828111615a935790509050815550601b6060516020525f5260405f208054608051808201828110615a9357905090508155506060516040517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60805160a052602060a0a3565b60035460605260025460805260055460a0524260a05110156159a65760045460c0526017546040526158666101006130f4565b6101006040810190505160e0525f546381d18d876101205260a0514203670de0b6b3a7640000810281670de0b6b3a7640000820418615a9357905060e0518015615a9357808204905090508060ff1c615a93577f80000000000000000000000000000000000000000000000000000000000000008114615a93575f03610140526020610120602461013c845afa6158ff573d5f5f3e3d5ffd5b60203d10615a93576101209050516101005260c0516080518060011b818160011c18615a93579050808281188284100218905090506101005180670de0b6b3a764000003670de0b6b3a76400008111615a93579050808202811583838304141715615a93579050905060605161010051808202811583838304141715615a935790509050808201828110615a935790509050670de0b6b3a7640000810490508152506159ad565b6060518152505b565b6020615ba75f395f5163f851a440604052602060406004605c845afa6159d7573d5f5f3e3d5ffd5b3d602081183d602010021880604001606011615a93576040518060a01c615a9357608052506080905051331815615a7b5760208061010052600a60a0527f6f6e6c79206f776e65720000000000000000000000000000000000000000000060c05260a08161010001602a82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a060e0528060040160fcfd5b565b60805160605160401b60405160801b1717815250565b5f80fd2c4d2c122b3d2c4d2c4d2acd185e14fc00852c4d2c4d13200c7315db104d2861001810a41adf180c0373197c2a5c2b2103bd2c4d268401942c4d0f6211950cb813e72bad15a01a3b293b2ae918ef2bf717652c4d2c4d128f11682c4d2c4d2c4d2c4d2c4d2c4d2c4d2c4d03231a59189b21a319542b5913662b752c4d10f101b32c4d2b9119a8144c2c4d2a7725b22c4d000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000002540be400000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c59900000000000000000000000098ee851a00abee0d95d08cf4ca2bdce32aeaaf7f00000000000000000000000000000000000000000000000000000000000000075942205742544300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086379622d77627463000000000000000000000000000000000000000000000000

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

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