{"file_path":"contracts/LT.vy","creation_status":"success","source_code":"# @version 0.4.3\n\"\"\"\n@title LT\n@notice Implementation of leveraged liquidity for Yield Basis\n@author Scientia Spectra AG\n@license Copyright (c) 2025\n\"\"\"\nfrom ethereum.ercs import IERC20\nfrom snekmate.utils import math\n\nimplements: IERC20\n\n\ninterface IERC20Detailed:\n    def decimals() -> uint256: view\n\ninterface IERC20Slice:\n    def symbol() -> String[29]: view\n\ninterface LevAMM:\n    def _deposit(d_collateral: uint256, d_debt: uint256) -> OraclizedValue: nonpayable\n    def _withdraw(frac: uint256) -> Pair: nonpayable\n    def value_change(collateral_amount: uint256, borrowed_amount: uint256, is_deposit: bool) -> OraclizedValue: view\n    def fee() -> uint256: view\n    def value_oracle() -> OraclizedValue: view\n    def get_state() -> AMMState: view\n    def get_debt() -> uint256: view\n    def collateral_amount() -> uint256: view\n    def value_oracle_for(collateral: uint256, debt: uint256) -> OraclizedValue: view\n    def set_rate(rate: uint256) -> uint256: nonpayable\n    def collect_fees() -> uint256: nonpayable\n    def PRICE_ORACLE_CONTRACT() -> PriceOracle: view\n    def max_debt() -> uint256: view\n    def COLLATERAL() -> address: view\n    def STABLECOIN() -> address: view\n    def LT_CONTRACT() -> address: view\n    def set_killed(is_killed: bool): nonpayable\n    def check_nonreentrant(): nonpayable\n    def is_killed() -> bool: view\n    def set_fee(fee: uint256): nonpayable\n\ninterface CurveCryptoPool:\n    def add_liquidity(amounts: uint256[2], min_mint_amount: uint256, receiver: address, donation: bool) -> uint256: nonpayable\n    def remove_liquidity(amount: uint256, min_amounts: uint256[2]) -> uint256[2]: nonpayable\n    def lp_price() -> uint256: view\n    def get_virtual_price() -> uint256: view\n    def price_oracle() -> uint256: view\n    def decimals() -> uint256: view\n    def mid_fee() -> uint256: view\n    def totalSupply() -> uint256: view\n    def coins(i: uint256) -> address: view\n    def calc_token_amount(amounts: uint256[2], deposit: bool) -> uint256: view\n    def balances(i: uint256) -> uint256: view\n    def approve(_to: address, _value: uint256) -> bool: nonpayable\n    def transfer(_to: address, _value: uint256) -> bool: nonpayable\n    def transferFrom(_from: address, _to: address, _value: uint256) -> bool: nonpayable\n    def remove_liquidity_fixed_out(token_amount: uint256, i: uint256, amount_i: uint256, min_amount_j: uint256) -> uint256: nonpayable\n    def calc_withdraw_fixed_out(token_amount: uint256, i: uint256, amount_i: uint256) -> uint256: view\n\ninterface PriceOracle:\n    def price_w() -> uint256: nonpayable\n    def price() -> uint256: view\n    def AGG() -> address: view\n\ninterface Factory:\n    def admin() -> address: view\n    def emergency_admin() -> address: view\n    def fee_receiver() -> address: view\n    def gauge_controller() -> address: view\n    def min_admin_fee() -> uint256: view\n\n\nstruct AMMState:\n    collateral: uint256\n    debt: uint256\n    x0: uint256\n\nstruct Pair:\n    collateral: uint256\n    debt: uint256\n\nstruct OraclizedValue:\n    p_o: uint256\n    value: uint256\n\nstruct LiquidityValues:\n    admin: int256  # Can be negative\n    total: uint256\n    ideal_staked: uint256\n    staked: uint256\n\nstruct LiquidityValuesOut:\n    admin: int256  # Can be negative\n    total: uint256\n    ideal_staked: uint256\n    staked: uint256\n    staked_tokens: uint256\n    supply_tokens: uint256\n    token_reduction: int256\n\n\nevent SetStaker:\n    staker: indexed(address)\n\n\nevent WithdrawAdminFees:\n    receiver: address\n    amount: uint256\n\nevent AllocateStablecoins:\n    allocator: indexed(address)\n    stablecoin_allocation: uint256\n    stablecoin_allocated: uint256\n\nevent DistributeBorrowerFees:\n    sender: indexed(address)\n    amount: uint256\n    min_amount: uint256\n    discount: uint256\n\n\n# ERC4626 events\n\nevent Deposit:\n    sender: indexed(address)\n    owner: indexed(address)\n    assets: uint256\n    shares: uint256\n\nevent Withdraw:\n    sender: indexed(address)\n    receiver: indexed(address)\n    owner: indexed(address)\n    assets: uint256\n    shares: uint256\n\n\nevent SetAdmin:\n    admin: address\n\n\nCRYPTOPOOL: public(immutable(CurveCryptoPool))  # Liquidity like LP(TBTC/crvUSD)\nSTABLECOIN: public(immutable(IERC20))  # For example, crvUSD\nASSET_TOKEN: public(immutable(IERC20))  # For example, TBTC\n\nCRYPTOPOOL_N_COINS: constant(uint256) = 2\nFEE_CLAIM_DISCOUNT: constant(uint256) = 10**16\nMIN_SHARE_REMAINDER: constant(uint256) = 10**6  # We leave at least this much of shares if > 0\nSQRT_MIN_UNSTAKED_FRACTION: constant(int256) = 10**14  # == 1e-4, avoiding infinite APR and 0/0 errors\nMIN_STAKED_FOR_FEES: constant(int256) = 10**16\n\nadmin: public(address)\namm: public(LevAMM)\nagg: public(PriceOracle)\n\nstaker: public(address)\n\nliquidity: public(LiquidityValues)\n\nallowance: public(HashMap[address, HashMap[address, uint256]])\nbalanceOf: public(HashMap[address, uint256])\ntotalSupply: public(uint256)\ndecimals: public(constant(uint8)) = 18\n\nstablecoin_allocation: public(uint256)\nstablecoin_allocated: public(uint256)\n\n\n@deploy\ndef __init__(asset_token: IERC20, stablecoin: IERC20, cryptopool: CurveCryptoPool,\n             admin: address):\n    \"\"\"\n    @notice Initializer (can be performed by an EOA deployer or a factory)\n    @param asset_token Token which gets deposited. Can be collateral or can be not\n    @param stablecoin Stablecoin which gets \"granted\" to this contract to use for loans. Has to be 18 decimals\n    @param cryptopool Cryptopool LP collateral token\n    @param admin Admin which can set callbacks, stablecoin allocator and fee. Sensitive!\n    \"\"\"\n    # Example:\n    # asset_token = WBTC\n    # stablecoin = crvUSD\n    # cryptopool = WBTC LP\n\n    STABLECOIN = stablecoin\n    CRYPTOPOOL = cryptopool\n    ASSET_TOKEN = asset_token\n    self.admin = admin\n    assert extcall asset_token.approve(cryptopool.address, max_value(uint256), default_return_value=True)\n    assert extcall stablecoin.approve(cryptopool.address, max_value(uint256), default_return_value=True)\n    assert staticcall cryptopool.coins(0) == stablecoin.address\n    assert staticcall cryptopool.coins(1) == asset_token.address\n\n    # Twocrypto has no N_COINS public, so we check that coins(2) reverts\n    success: bool = False\n    res: Bytes[32] = empty(Bytes[32])\n    success, res = raw_call(\n        cryptopool.address,\n        abi_encode(CRYPTOPOOL_N_COINS, method_id=method_id(\"coins(uint256)\")),\n        max_outsize=32,\n        is_static_call=True,\n        revert_on_failure=False)\n    assert not success, \"N>2\"\n\n\n@internal\n@view\ndef _check_admin():\n    admin: address = self.admin\n    if admin.is_contract:\n        assert msg.sender == admin or msg.sender == staticcall Factory(admin).admin(), \"Access\"\n    else:\n        assert msg.sender == admin, \"Access\"\n\n\n@internal\n@view\ndef _price_oracle() -> uint256:\n    return staticcall CRYPTOPOOL.price_oracle() * staticcall self.agg.price() // 10**18\n\n\n@internal\ndef _price_oracle_w() -> uint256:\n    return staticcall CRYPTOPOOL.price_oracle() * extcall self.agg.price_w() // 10**18\n\n\n@internal\ndef _checkpoint_gauge():\n    \"\"\"\n    @notice Checkpoint a gauge if any to prevent flash loan attacks on reward distribution, reverts are ignored\n    \"\"\"\n    gauge: address = self.staker\n    if gauge != empty(address):\n        admin: address = self.admin\n        if admin.is_contract:\n            gc: address = staticcall Factory(admin).gauge_controller()\n            if gc != empty(address):\n                success: bool = raw_call(\n                    gc,\n                    abi_encode(gauge, method_id=method_id(\"checkpoint(address)\")),\n                    is_static_call=False,\n                    revert_on_failure=False)\n                if not success:\n                    # Try again but with fixed gas. This will just make TX fail if one tries to manipulate\n                    # the gas attached to the call while NOT inflating the gas estimate if the call does not revert\n                    assert msg.gas >= 200_000, \"GAS\"\n                    success = raw_call(\n                        gc,\n                        abi_encode(gauge, method_id=method_id(\"checkpoint(address)\")),\n                        gas=200_000,\n                        is_static_call=False,\n                        revert_on_failure=False)\n\n\n@internal\n@view\ndef _min_admin_fee() -> uint256:\n    admin: address = self.admin\n    if admin.is_contract:\n        return staticcall Factory(admin).min_admin_fee()\n    else:\n        return 0\n\n\n@external\n@view\ndef min_admin_fee() -> uint256:\n    return self._min_admin_fee()\n\n\n@internal\n@pure\ndef mul_div_signed(x: int256, y: int256, denominator: int256) -> int256:\n    if denominator == 0:\n        return 0\n\n    value: int256 = convert(\n        math._mul_div(\n            convert(abs(x), uint256),\n            convert(abs(y), uint256),\n            convert(abs(denominator), uint256),\n            False),\n        int256)\n\n    if ((x < 0) != (y < 0)) != (denominator < 0):\n        value = -value\n\n    return value\n\n\n@internal\n@view\ndef _calculate_values(p_o: uint256) -> LiquidityValuesOut:\n    prev: LiquidityValues = self.liquidity\n    staker: address = self.staker\n    staked: int256 = 0\n    if staker != empty(address):\n        staked = convert(self.balanceOf[self.staker], int256)\n    supply: int256 = convert(self.totalSupply, int256)\n    # staked is guaranteed to be <= supply\n\n    f_a: int256 = convert(\n        10**18 - (10**18 - self._min_admin_fee()) * isqrt(convert(10**36 - staked * 10**36 // supply, uint256)) // 10**18,\n        int256)\n\n    cur_value: int256 = convert((staticcall self.amm.value_oracle()).value * 10**18 // p_o, int256)\n    prev_value: int256 = convert(prev.total, int256)\n    value_change: int256 = cur_value - (prev_value + prev.admin)\n\n    v_st: int256 = convert(prev.staked, int256)\n    v_st_ideal: int256 = convert(prev.ideal_staked, int256)\n    # ideal_staked is set when some tokens are transferred to staker address\n\n    # _36 postifix is to emphasize that the value is 1e36-based, not 1e18, for type tracking purposes\n\n    # Admin fees are earned only when all losses are paid off\n    dv_use_36: int256 = 0\n    v_st_loss: int256 = max(v_st_ideal - v_st, 0)\n    if staked >= MIN_STAKED_FOR_FEES:\n        if value_change > 0:\n            # Admin fee is only charged once the loss if fully paid off\n            v_loss: int256 = min(value_change, v_st_loss * supply // staked)\n            dv_use_36 = v_loss * 10**18 + (value_change - v_loss) * (10**18 - f_a)\n        else:\n            # Admin doesn't pay for value loss\n            dv_use_36 = value_change * 10**18\n    else:\n        # If stakeda part is small - positive admin fees are charged on profits and negative on losses\n        dv_use_36 = value_change * (10**18 - f_a)\n\n    prev.admin += (value_change - dv_use_36 // 10**18)\n\n    # dv_s is guaranteed to be <= dv_use\n    # if staked < supply (not exactly 100.0% staked) - dv_s is strictly < dv_use\n    dv_s_36: int256 = self.mul_div_signed(dv_use_36, staked, supply)\n    if dv_use_36 > 0:\n        dv_s_36 = min(dv_s_36, v_st_loss * 10**18)\n\n    # new_staked_value is guaranteed to be <= new_total_value\n    new_total_value_36: int256 = max(prev_value * 10**18 + dv_use_36, 0)\n    new_staked_value_36: int256 = max(v_st * 10**18 + dv_s_36, 0)\n\n    # Solution of:\n    # staked - token_reduction       new_staked_value\n    # -------------------------  =  -------------------\n    # supply - token_reduction         new_total_value\n    #\n    # the result:\n    #                      new_total_value * staked - new_staked_value * supply\n    # token_reduction  =  ------------------------------------------------------\n    #                               new_total_value - new_staked_value\n    #\n    # When eps = (supply - staked) / supply << 1, it comes down to:\n    # token_reduction = value_change / total_value * (1.0 - min_admin_fee) / sqrt(eps) * supply\n    # So when eps < 1e-8 - we'll limit token_reduction\n\n    # If denominator is 0 -> token_reduction = 0 (not a revert)\n\n    token_reduction: int256 = new_total_value_36 - new_staked_value_36  # Denominator\n    token_reduction = self.mul_div_signed(new_total_value_36, staked, token_reduction) - self.mul_div_signed(new_staked_value_36, supply, token_reduction)\n\n    max_token_reduction: int256 = abs(value_change * supply // (prev_value + value_change + 1) * (10**18 - f_a) // SQRT_MIN_UNSTAKED_FRACTION)\n\n    # let's leave at least 1 LP token for staked and for total\n    if staked > 0:\n        token_reduction = min(token_reduction, staked - 1)\n    if supply > 0:\n        token_reduction = min(token_reduction, supply - 1)\n    # But most likely it's this condition to apply\n    if token_reduction >= 0:\n        token_reduction = min(token_reduction, max_token_reduction)\n    else:\n        token_reduction = max(token_reduction, -max_token_reduction)\n    # And don't allow negatives if denominator was too small\n    if new_total_value_36 - new_staked_value_36 < 10**4 * 10**18:\n        token_reduction = max(token_reduction, 0)\n\n    # Supply changes each time:\n    # value split reduces the amount of staked tokens (but not others),\n    # and this also reduces the supply of LP tokens\n\n    return LiquidityValuesOut(\n        admin=prev.admin,\n        total=convert(new_total_value_36 // 10**18, uint256),\n        ideal_staked=prev.ideal_staked,\n        staked=convert(new_staked_value_36 // 10**18, uint256),\n        staked_tokens=convert(staked - token_reduction, uint256),\n        supply_tokens=convert(supply - token_reduction, uint256),\n        token_reduction=token_reduction\n    )\n\n\n@internal\ndef _log_token_reduction(staker: address, token_reduction: int256):\n    if token_reduction < 0:\n        log IERC20.Transfer(sender=empty(address), receiver=staker, value=convert(-token_reduction, uint256))\n    if token_reduction > 0:\n        log IERC20.Transfer(sender=staker, receiver=empty(address), value=convert(token_reduction, uint256))\n\n\n@external\n@view\n@nonreentrant\ndef preview_deposit(assets: uint256, debt: uint256) -> uint256:\n    \"\"\"\n    @notice Returns the amount of shares which can be obtained upon depositing assets, including slippage.\n            Reverts if amounts are too high\n    @param assets Amount of crypto to deposit\n    @param debt Amount of stables to borrow for MMing (approx same value as crypto)\n    \"\"\"\n    lp_tokens: uint256 = staticcall CRYPTOPOOL.calc_token_amount([debt, assets], True)\n    supply: uint256 = self.totalSupply\n    p_o: uint256 = self._price_oracle()\n    amm: LevAMM = self.amm\n    amm_max_debt: uint256 = staticcall amm.max_debt() // 2\n\n    if supply > 0:\n        liquidity: LiquidityValuesOut = self._calculate_values(p_o)\n        if liquidity.total > 0:\n            v: OraclizedValue = staticcall amm.value_change(lp_tokens, debt, True)\n            if amm_max_debt < v.value:\n                raise \"Debt too high\"\n            # Liquidity contains admin fees, so we need to subtract\n            # If admin fees are negative - we get LESS LP tokens\n            # value_before = v.value_before - liquidity.admin = total\n            value_after: uint256 = convert(convert(v.value * 10**18 // p_o, int256) - liquidity.admin, uint256)\n            return liquidity.supply_tokens * value_after // liquidity.total - liquidity.supply_tokens\n\n    v: OraclizedValue = staticcall amm.value_oracle_for(lp_tokens, debt)\n    if amm_max_debt < v.value:\n        raise \"Debt too high\"\n    return v.value * 10**18 // p_o\n\n\n@external\n@view\n@nonreentrant\ndef preview_withdraw(tokens: uint256) -> uint256:\n    \"\"\"\n    @notice Returns the amount of assets which can be obtained upon withdrawing from tokens\n    \"\"\"\n    v: LiquidityValuesOut = self._calculate_values(self._price_oracle())\n    state: AMMState = staticcall self.amm.get_state()\n    # Total does NOT include uncollected admin fees\n    # however we account only for positive admin balance. This \"socializes\" losses if they happen\n    admin_balance: uint256 = convert(max(v.admin, 0), uint256)\n    frac: uint256 = 10**18 * v.total // (v.total + admin_balance) * tokens // v.supply_tokens\n    withdrawn_lp: uint256 = state.collateral * frac // 10**18\n    withdrawn_debt: uint256 = state.debt * frac // 10**18\n    return staticcall CRYPTOPOOL.calc_withdraw_fixed_out(withdrawn_lp, 0, withdrawn_debt)\n\n\n@external\n@nonreentrant\ndef deposit(assets: uint256, debt: uint256, min_shares: uint256, receiver: address = msg.sender) -> uint256:\n    \"\"\"\n    @notice Method to deposit assets (e.g. like BTC) to receive shares (e.g. like yield-bearing BTC)\n    @param assets Amount of assets to deposit\n    @param debt Amount of debt for AMM to take (approximately BTC * btc_price)\n    @param min_shares Minimal amount of shares to receive (important to calculate to exclude sandwich attacks)\n    @param receiver Receiver of the shares who is optional. If not specified - receiver is the sender\n    \"\"\"\n    staker: address = self.staker\n    assert receiver != staker, \"Deposit to staker\"\n\n    amm: LevAMM = self.amm\n    assert extcall STABLECOIN.transferFrom(amm.address, self, debt, default_return_value=True)\n    assert extcall ASSET_TOKEN.transferFrom(msg.sender, self, assets, default_return_value=True)\n    lp_tokens: uint256 = extcall CRYPTOPOOL.add_liquidity([debt, assets], 0, amm.address, False)\n    p_o: uint256 = self._price_oracle_w()\n\n    supply: uint256 = self.totalSupply\n    shares: uint256 = 0\n\n    liquidity_values: LiquidityValuesOut = empty(LiquidityValuesOut)\n    if supply > 0:\n        liquidity_values = self._calculate_values(p_o)\n\n    v: OraclizedValue = extcall amm._deposit(lp_tokens, debt)\n    value_after: uint256 = v.value * 10**18 // p_o\n\n    # Value is measured in USD\n    # Do not allow value to become larger than HALF of the available stablecoins after the deposit\n    # If value becomes too large - we don't allow to deposit more to have a buffer when the price rises\n    assert staticcall amm.max_debt() // 2 >= v.value, \"Debt too high\"\n\n    if supply > 0 and liquidity_values.total > 0:\n        supply = liquidity_values.supply_tokens\n        self.liquidity.admin = liquidity_values.admin\n        value_before: uint256 = liquidity_values.total\n        value_after = convert(convert(value_after, int256) - liquidity_values.admin, uint256)\n        self.liquidity.total = value_after\n        self.liquidity.staked = liquidity_values.staked\n        self.totalSupply = liquidity_values.supply_tokens  # will be increased by mint\n        if staker != empty(address):\n            self.balanceOf[staker] = liquidity_values.staked_tokens\n            self._log_token_reduction(staker, liquidity_values.token_reduction)\n        # ideal_staked is only changed when we transfer coins to staker\n        shares = supply * value_after // value_before - supply\n\n    else:\n        # Initial value/shares ratio is EXACTLY 1.0 in collateral units\n        # Value is measured in USD\n        shares = value_after\n        # self.liquidity.admin is 0 at start but can be rolled over if everything was withdrawn\n        self.liquidity.ideal_staked = 0         # Likely already 0 since supply was 0\n        self.liquidity.staked = 0               # Same: nothing staked when supply is 0\n        self.liquidity.total = shares + supply  # 1 share = 1 crypto at first deposit\n        self.liquidity.admin = 0                # if we had admin fees - give them to the first depositor; simpler to handle\n        if self.balanceOf[staker] > 0:\n            log IERC20.Transfer(sender=staker, receiver=empty(address), value=self.balanceOf[staker])\n        self.balanceOf[staker] = 0\n\n    assert shares + supply >= MIN_SHARE_REMAINDER, \"Remainder too small\"\n    assert shares >= min_shares, \"Slippage\"\n\n    self._mint(receiver, shares)\n    self._checkpoint_gauge()\n    log Deposit(sender=msg.sender, owner=receiver, assets=assets, shares=shares)\n    self._distribute_borrower_fees(FEE_CLAIM_DISCOUNT)\n    return shares\n\n\n@external\n@nonreentrant\ndef withdraw(shares: uint256, min_assets: uint256, receiver: address = msg.sender) -> uint256:\n    \"\"\"\n    @notice Method to withdraw assets (e.g. like BTC) by spending shares (e.g. like yield-bearing BTC)\n    @param shares Shares to withdraw\n    @param min_assets Minimal amount of assets to receive (important to calculate to exclude sandwich attacks)\n    @param receiver Receiver of the assets who is optional. If not specified - receiver is the sender\n    \"\"\"\n    assert shares > 0, \"Withdrawing nothing\"\n\n    staker: address = self.staker\n    assert staker not in [msg.sender, receiver], \"Withdraw to/from staker\"\n\n    assert not (staticcall self.amm.is_killed()), \"We're dead. Use emergency_withdraw\"\n\n    amm: LevAMM = self.amm\n    liquidity_values: LiquidityValuesOut = self._calculate_values(self._price_oracle_w())\n    supply: uint256 = liquidity_values.supply_tokens\n    self.liquidity.admin = liquidity_values.admin\n    # self.liquidity.total = liquidity_values.total  no need to update since we will record this value later\n    self.liquidity.staked = liquidity_values.staked\n    self.totalSupply = supply\n\n    assert supply >= MIN_SHARE_REMAINDER + shares or supply == shares, \"Remainder too small\"\n\n    if staker != empty(address):\n        self.balanceOf[staker] = liquidity_values.staked_tokens\n        self._log_token_reduction(staker, liquidity_values.token_reduction)\n\n    admin_balance: uint256 = convert(max(liquidity_values.admin, 0), uint256)\n\n    withdrawn: Pair = extcall amm._withdraw(10**18 * liquidity_values.total // (liquidity_values.total + admin_balance) * shares // supply)\n    assert extcall CRYPTOPOOL.transferFrom(amm.address, self, withdrawn.collateral, default_return_value=True)\n    crypto_received: uint256 = extcall CRYPTOPOOL.remove_liquidity_fixed_out(withdrawn.collateral, 0, withdrawn.debt, 0)\n\n    self._burn(msg.sender, shares)  # Changes self.totalSupply\n    self.liquidity.total = liquidity_values.total * (supply - shares) // supply\n    if liquidity_values.admin < 0:\n        # If admin fees are negative - we are skipping them, so reduce proportionally\n        self.liquidity.admin = liquidity_values.admin * convert(supply - shares, int256) // convert(supply, int256)\n    assert crypto_received >= min_assets, \"Slippage\"\n    assert extcall STABLECOIN.transfer(amm.address, withdrawn.debt, default_return_value=True)\n    assert extcall ASSET_TOKEN.transfer(receiver, crypto_received, default_return_value=True)\n\n    self._checkpoint_gauge()\n    log Withdraw(sender=msg.sender, receiver=receiver, owner=msg.sender, assets=crypto_received, shares=shares)\n    self._distribute_borrower_fees(FEE_CLAIM_DISCOUNT)\n    return crypto_received\n\n\n@external\n@view\n@nonreentrant\ndef preview_emergency_withdraw(shares: uint256) -> (uint256, int256):\n    \"\"\"\n    @notice Method to simulate repay of the debt from the wallet and withdraw what is in the AMM. Does not use heavy math but\n            does not necessarily work as single asset withdrawal\n    @param shares Shares to withdraw\n    @return (unsigned collateral, signed stables). If stables < 0 - we need to bring them\n    \"\"\"\n    supply: uint256 = 0\n    lv: LiquidityValuesOut = empty(LiquidityValuesOut)\n    amm: LevAMM = self.amm\n\n    if staticcall amm.is_killed():\n        supply = self.totalSupply\n    else:\n        lv = self._calculate_values(self._price_oracle())\n        supply = lv.supply_tokens\n\n    frac: uint256 = 10**18 * shares // supply\n    if lv.admin > 0 and lv.total != 0:\n        frac = frac * lv.total // (convert(lv.admin, uint256) + lv.total)\n\n    lp_collateral: uint256 = (staticcall amm.collateral_amount()) * frac // 10**18\n    debt: int256 = convert(math._ceil_div((staticcall amm.get_debt()) * frac, 10**18), int256)\n\n    cryptopool_supply: uint256 = staticcall CRYPTOPOOL.totalSupply()\n    withdraw_amounts: uint256[2] = [staticcall CRYPTOPOOL.balances(0), staticcall CRYPTOPOOL.balances(1)]\n    withdraw_amounts = [\n        lp_collateral * withdraw_amounts[0] // cryptopool_supply,\n        lp_collateral * withdraw_amounts[1] // cryptopool_supply\n    ]\n\n    return (withdraw_amounts[1], convert(withdraw_amounts[0], int256) - debt)\n\n\n@external\n@nonreentrant\ndef emergency_withdraw(shares: uint256, receiver: address = msg.sender, owner: address = msg.sender) -> (uint256, int256):\n    \"\"\"\n    @notice Method to repay the debt from the wallet and withdraw what is in the AMM. Does not use heavy math but\n            does not necessarily work as single asset withdrawal. Minimal output is not specified: convexity of\n            bonding curves ensures that attackers can only lose value, not gain\n    @param shares Shares to withdraw\n    @param receiver Receiver of the assets who is optional. If not specified - receiver is the sender\n    @return (unsigned asset, signed stables). If stables < 0 - we need to bring them\n    \"\"\"\n    staker: address = self.staker\n    assert staker not in [owner, receiver], \"Withdraw to/from staker\"\n\n    supply: uint256 = 0\n    lv: LiquidityValuesOut = empty(LiquidityValuesOut)\n    amm: LevAMM = self.amm\n    killed: bool = staticcall amm.is_killed()\n\n    if killed:\n        # If killed:\n        admin: address = self.admin\n        if admin.is_contract:\n            if msg.sender == staticcall Factory(admin).emergency_admin():\n                # Only emergency admin is allowed to withdraw for others, but then only transfer to themselves\n                assert receiver == owner, \"receiver\"\n            else:\n                # If sender is a different smart contract - it must be the owner\n                assert owner == msg.sender, \"owner\"\n        else:\n            if msg.sender == admin:\n                # If admin is EOA - it can withdraw for receivers when killed, but only send to themselves\n                assert receiver == owner, \"receiver\"\n            else:\n                # If EOA caller is not admin - it must be the owner\n                assert owner == msg.sender, \"owner\"\n\n        supply = self.totalSupply\n\n    else:\n        # If not killed - only owner is allowed to work with their LP\n        assert owner == msg.sender, \"Not killed\"\n\n        lv = self._calculate_values(self._price_oracle_w())\n        supply = lv.supply_tokens\n        self.liquidity.admin = lv.admin\n        self.liquidity.total = lv.total\n        self.liquidity.staked = lv.staked\n        self.totalSupply = supply\n        if staker != empty(address):\n            self.balanceOf[staker] = lv.staked_tokens\n            self._log_token_reduction(staker, lv.token_reduction)\n\n    assert supply >= MIN_SHARE_REMAINDER + shares or supply == shares, \"Remainder too small\"\n\n    frac: uint256 = 10**18 * shares // supply\n    frac_clean: int256 = convert(frac, int256)\n    if lv.admin > 0 and lv.total != 0:\n        frac = frac * lv.total // (convert(lv.admin, uint256) + lv.total)\n\n    withdrawn_levamm: Pair = extcall amm._withdraw(frac)\n    assert extcall CRYPTOPOOL.transferFrom(amm.address, self, withdrawn_levamm.collateral, default_return_value=True)\n    withdrawn_cswap: uint256[2] = extcall CRYPTOPOOL.remove_liquidity(withdrawn_levamm.collateral, [0, 0])\n    stables_to_return: int256 = convert(withdrawn_cswap[0], int256) - convert(withdrawn_levamm.debt, int256)\n\n    self._burn(owner, shares)\n\n    self.liquidity.total = self.liquidity.total * (supply - shares) // supply\n    if self.liquidity.admin < 0 or killed:\n        self.liquidity.admin = self.liquidity.admin * (10**18 - frac_clean) // 10**18\n\n    if stables_to_return > 0:\n        assert extcall STABLECOIN.transfer(receiver, convert(stables_to_return, uint256), default_return_value=True)\n    elif stables_to_return < 0:\n        assert extcall STABLECOIN.transferFrom(msg.sender, self, convert(-stables_to_return, uint256), default_return_value=True)\n    assert extcall STABLECOIN.transfer(amm.address, withdrawn_levamm.debt, default_return_value=True)\n    assert extcall ASSET_TOKEN.transfer(receiver, withdrawn_cswap[1], default_return_value=True)\n\n    if not killed:\n        self._checkpoint_gauge()\n\n    return (withdrawn_cswap[1], stables_to_return)\n\n\n@external\ndef checkpoint_staker_rebase():\n    staker: address = self.staker\n    killed: bool = staticcall self.amm.is_killed()\n\n    if msg.sender == staker and not killed:\n        lv: LiquidityValuesOut = self._calculate_values(self._price_oracle_w())\n        self.liquidity.admin = lv.admin\n        self.liquidity.total = lv.total\n        self.liquidity.staked = lv.staked\n        self.totalSupply = lv.supply_tokens\n        self.balanceOf[staker] = lv.staked_tokens\n        self._log_token_reduction(staker, lv.token_reduction)\n\n\n@external\n@view\ndef pricePerShare() -> uint256:\n    \"\"\"\n    Non-manipulatable \"fair price per share\" oracle\n    \"\"\"\n    v: LiquidityValuesOut = self._calculate_values(self._price_oracle())\n    if v.supply_tokens == 0:\n        return 10**18\n    else:\n        return v.total * 10**18 // v.supply_tokens\n\n\n@external\n@nonreentrant\ndef set_amm(amm: LevAMM):\n    \"\"\"\n    Set AMM: only allowed once (at init time)\n    \"\"\"\n    self._check_admin()\n    assert self.amm == empty(LevAMM), \"Already set\"\n    assert staticcall amm.STABLECOIN() == STABLECOIN.address\n    assert staticcall amm.COLLATERAL() == CRYPTOPOOL.address\n    assert staticcall amm.LT_CONTRACT() == self\n    self.amm = amm\n    self.agg = PriceOracle(staticcall (staticcall amm.PRICE_ORACLE_CONTRACT()).AGG())\n\n\n@external\n@nonreentrant\ndef set_admin(new_admin: address):\n    \"\"\"\n    Set admin to factory or EOA\n    \"\"\"\n    self._check_admin()\n\n    # Sanity check for the new admin\n    if new_admin.is_contract:\n        check_address: address = staticcall Factory(new_admin).admin()\n        check_address = staticcall Factory(new_admin).emergency_admin()\n        check_address = staticcall Factory(new_admin).fee_receiver()\n        check_address = staticcall Factory(new_admin).gauge_controller()\n        check_uint: uint256 = staticcall Factory(new_admin).min_admin_fee()\n\n    self.admin = new_admin\n    log SetAdmin(admin=new_admin)\n\n\n@external\n@nonreentrant\ndef set_rate(rate: uint256):\n    \"\"\"\n    @notice Set borrow rate == donation rate for the AMM in units of 1e18-based fraction per second\n    \"\"\"\n    self._check_admin()\n    extcall self.amm.set_rate(rate)\n\n\n@external\n@nonreentrant\ndef set_amm_fee(fee: uint256):\n    \"\"\"\n    @notice Set 1e18-based AMM fee\n    \"\"\"\n    self._check_admin()\n    extcall self.amm.set_fee(fee)\n\n\n@external\n@nonreentrant\ndef allocate_stablecoins(limit: uint256 = max_value(uint256)):\n    \"\"\"\n    @notice This method has to be used once this contract has received allocation of stablecoins\n    @param limit Limit to allocate for this pool from this allocator. Max uint256 = do not change\n    \"\"\"\n    allocator: address = self.admin\n    allocation: uint256 = limit\n    allocated: uint256 = self.stablecoin_allocated\n    if limit == max_value(uint256):\n        allocation = self.stablecoin_allocation\n    else:\n        self._check_admin()\n        self.stablecoin_allocation = limit\n\n    extcall self.amm.check_nonreentrant()\n\n    if allocation > allocated:\n        # Assume that allocator has everything\n        assert extcall STABLECOIN.transferFrom(allocator, self.amm.address, allocation - allocated, default_return_value=True)\n        self.stablecoin_allocated = allocation\n\n    elif allocation < allocated:\n        lp_price: uint256 = extcall (staticcall self.amm.PRICE_ORACLE_CONTRACT()).price_w()\n        assert allocation >= lp_price * (staticcall self.amm.collateral_amount()) // 10**18, \"Not enough stables\"\n        to_transfer: uint256 = min(allocated - allocation, staticcall STABLECOIN.balanceOf(self.amm.address))\n        allocated -= to_transfer\n        assert extcall STABLECOIN.transferFrom(self.amm.address, allocator, to_transfer, default_return_value=True)\n        self.stablecoin_allocated = allocated\n\n    log AllocateStablecoins(allocator=allocator, stablecoin_allocation=allocation, stablecoin_allocated=allocated)\n\n\n@internal\ndef _distribute_borrower_fees(discount: uint256):\n    amm: LevAMM = self.amm\n    if discount > FEE_CLAIM_DISCOUNT:\n        self._check_admin()\n    if msg.sender != amm.address:\n        extcall amm.collect_fees()\n    amount: uint256 = staticcall STABLECOIN.balanceOf(self)\n    if amount > 0:\n        # We price to the stablecoin we use, not the aggregated USD here, and this is correct\n        # It is possible to have a temporary denial of service here which, though, does not affect emergency\n        # withdrawal. It only can happen if price moves too fast. It does not prevent cryptopool from\n        # being traded and returning back to normal operation\n        min_amount: uint256 = (10**18 - discount) * amount // staticcall CRYPTOPOOL.lp_price()\n        extcall CRYPTOPOOL.add_liquidity([amount, 0], min_amount, empty(address), True)\n        log DistributeBorrowerFees(sender=msg.sender, amount=amount, min_amount=min_amount, discount=discount)\n\n\n@external\n@nonreentrant\ndef distribute_borrower_fees(discount: uint256 = FEE_CLAIM_DISCOUNT):\n    \"\"\"\n    @notice Distribute borrower fees to the AMM\n    @param discount Optional discount (1% by default): only settable by admin\n    \"\"\"\n    self._distribute_borrower_fees(discount)\n\n\n@external\n@nonreentrant\ndef withdraw_admin_fees():\n    \"\"\"\n    @notice Withdraw admin fees and distribute to the DAO\n    \"\"\"\n    admin: address = self.admin\n    assert admin.is_contract, \"Need factory\"\n    assert not staticcall self.amm.is_killed(), \"Killed\"\n    extcall self.amm.check_nonreentrant()\n\n    fee_receiver: address = staticcall Factory(admin).fee_receiver()\n    assert fee_receiver != empty(address), \"No fee_receiver\"\n\n    staker: address = self.staker\n    assert fee_receiver != staker, \"Staker=fee_receiver\"\n\n    v: LiquidityValuesOut = self._calculate_values(self._price_oracle_w())\n    assert v.admin >= 0, \"Loss made admin fee negative\"\n    self.totalSupply = v.supply_tokens\n    # Mint YB tokens to fee receiver and burn the untokenized admin buffer at the same time\n    # fee_receiver is just a normal user\n    new_total: uint256 = v.total + convert(v.admin, uint256)\n    to_mint: uint256 = v.supply_tokens * new_total // v.total - v.supply_tokens\n    self._mint(fee_receiver, to_mint)\n    self.liquidity.total = new_total\n    self.liquidity.admin = 0\n    self.liquidity.staked = v.staked\n    if staker != empty(address):\n        self.balanceOf[staker] = v.staked_tokens\n        self._log_token_reduction(staker, v.token_reduction)\n\n    log WithdrawAdminFees(receiver=fee_receiver, amount=to_mint)\n\n\n@external\n@nonreentrant\ndef set_staker(staker: address):\n    \"\"\"\n    @notice Set staker contract - allowed only once\n    \"\"\"\n    assert self.staker == empty(address), \"Staker already set\"\n    assert staker != empty(address)\n    self._check_admin()\n\n    staker_balance: uint256 = self.balanceOf[staker]\n    if staker_balance > 0:\n        # Take that all as admin fee, staker should not have this\n        fee_receiver: address = staticcall Factory(self.admin).fee_receiver()\n        assert staker != fee_receiver, \"Staker=fee_receiver\"\n        self._transfer(staker, fee_receiver, staker_balance)\n\n    self.staker = staker\n    log SetStaker(staker=staker)\n\n\n@external\ndef set_killed(is_killed: bool):\n    \"\"\"\n    @notice Kill or unkill the pool\n    \"\"\"\n    admin: address = self.admin\n    if admin.is_contract:\n        assert msg.sender in [admin, staticcall Factory(admin).admin(), staticcall Factory(admin).emergency_admin()], \"Access\"\n    else:\n        assert msg.sender == admin, \"Access\"\n    extcall self.amm.set_killed(is_killed)\n\n\n# ERC20 methods\n\n@external\n@view\ndef symbol() -> String[32]:\n    return concat('yb-', staticcall IERC20Slice(ASSET_TOKEN.address).symbol())\n\n\n@external\n@view\ndef name() -> String[58]:\n    return concat('Yield Basis liquidity for ', staticcall IERC20Slice(ASSET_TOKEN.address).symbol())\n\n\n@internal\ndef _approve(_owner: address, _spender: address, _value: uint256):\n    self.allowance[_owner][_spender] = _value\n\n    log IERC20.Approval(owner=_owner, spender=_spender, value=_value)\n\n\n@internal\ndef _burn(_from: address, _value: uint256):\n    self.balanceOf[_from] -= _value\n    self.totalSupply -= _value\n\n    log IERC20.Transfer(sender=_from, receiver=empty(address), value=_value)\n\n\n@internal\ndef _mint(_to: address, _value: uint256):\n    self.balanceOf[_to] += _value\n    self.totalSupply += _value\n\n    log IERC20.Transfer(sender=empty(address), receiver=_to, value=_value)\n\n\n@internal\ndef _transfer(_from: address, _to: address, _value: uint256):\n    assert _to not in [self, empty(address)]\n\n    staker: address = self.staker\n    staker_used: bool = (staker != empty(address) and staker in [_from, _to])\n    if staker_used:\n        assert _from != _to\n        liquidity: LiquidityValuesOut = empty(LiquidityValuesOut)\n\n        if msg.sender == staker or staticcall self.amm.is_killed():\n            # If sender is staker - it's mint/redeem/deposit/withdraw\n            # where we did checkpoint_staker_rebase() in the same tx\n            liquidity.ideal_staked = self.liquidity.ideal_staked\n            liquidity.staked = self.liquidity.staked\n            liquidity.total = self.liquidity.total\n            liquidity.supply_tokens = self.totalSupply\n            liquidity.staked_tokens = self.balanceOf[staker]\n        else:\n            liquidity = self._calculate_values(self._price_oracle_w())\n            self.liquidity.admin = liquidity.admin\n            self.liquidity.total = liquidity.total\n            self.totalSupply = liquidity.supply_tokens\n            self.balanceOf[staker] = liquidity.staked_tokens\n            self._log_token_reduction(staker, liquidity.token_reduction)\n\n        if _from == staker:\n            # Reduce the staked part\n            # change by 0 if no supply_tokens or stake_tokens found\n            liquidity.staked -= unsafe_div(liquidity.total * _value, liquidity.supply_tokens)\n            liquidity.ideal_staked = unsafe_div(liquidity.ideal_staked * (liquidity.staked_tokens - _value), liquidity.staked_tokens)\n        elif _to == staker:\n            # Increase the staked part\n            d_staked_value: uint256 = liquidity.total * _value // liquidity.supply_tokens\n            liquidity.staked += d_staked_value\n            if liquidity.staked_tokens > 10**10:\n                liquidity.ideal_staked = liquidity.ideal_staked * (liquidity.staked_tokens + _value) // liquidity.staked_tokens\n            else:\n                # To exclude division by zero and numerical noise errors\n                liquidity.ideal_staked += d_staked_value\n\n        self.liquidity.staked = liquidity.staked\n        self.liquidity.ideal_staked = liquidity.ideal_staked\n\n    self.balanceOf[_from] -= _value\n    self.balanceOf[_to] += _value\n\n    if staker_used and msg.sender != staker:\n        self._checkpoint_gauge()\n\n    log IERC20.Transfer(sender=_from, receiver=_to, value=_value)\n\n\n@external\ndef transferFrom(_from: address, _to: address, _value: uint256) -> bool:\n    \"\"\"\n    @notice Transfer tokens from one account to another.\n    @dev The caller needs to have an allowance from account `_from` greater than or\n        equal to the value being transferred. An allowance equal to the uint256 type's\n        maximum, is considered infinite and does not decrease.\n    @param _from The account which tokens will be spent from.\n    @param _to The account which tokens will be sent to.\n    @param _value The amount of tokens to be transferred.\n    \"\"\"\n    allowance: uint256 = self.allowance[_from][msg.sender]\n    if allowance != max_value(uint256):\n        self._approve(_from, msg.sender, allowance - _value)\n\n    self._transfer(_from, _to, _value)\n    return True\n\n\n@external\ndef transfer(_to: address, _value: uint256) -> bool:\n    \"\"\"\n    @notice Transfer tokens to `_to`.\n    @param _to The account to transfer tokens to.\n    @param _value The amount of tokens to transfer.\n    \"\"\"\n    self._transfer(msg.sender, _to, _value)\n    return True\n\n\n@external\ndef approve(_spender: address, _value: uint256) -> bool:\n    \"\"\"\n    @notice Allow `_spender` to transfer up to `_value` amount of tokens from the caller's account.\n    @dev Non-zero to non-zero approvals are allowed, but should be used cautiously. The methods\n        increaseAllowance + decreaseAllowance are available to prevent any front-running that\n        may occur.\n    @param _spender The account permitted to spend up to `_value` amount of caller's funds.\n    @param _value The amount of tokens `_spender` is allowed to spend.\n    \"\"\"\n    self._approve(msg.sender, _spender, _value)\n    return True\n\n\n@external\n@view\ndef is_killed() -> bool:\n    return staticcall self.amm.is_killed()\n","deployed_bytecode":"0x5f3560e01c6002602b820660011b61589a01601e395f51565b638c5710c98118613ead573461589657602061003460a0613eb1565b60a0f35b633911670481186103c457604436103417615896575f5c6001146158965760206158f05f395f5163ed8e84f361056052602435610580526004356105a05260016105c0526020610560606461057c845afa610095573d5f5f3e3d5ffd5b60203d106158965761056090505161054052600a54610560526100b96105a0613efd565b6105a051610580526001546105a0526105a05163638a1e8d6105e05260206105e060046105fc845afa6100ee573d5f5f3e3d5ffd5b60203d10615896576105e09050518060011c90506105c05261056051156102b55761058051610280526101226106c061441b565b6106c060e0816105e05e5061060051156102b5576105a05163059eaa78610700526105405161072052602435610740526001610760526040610700606461071c845afa610171573d5f5f3e3d5ffd5b60403d106158965761070090506040816106c05e506106e0516105c051101561020c5760208061076052600d610700527f4465627420746f6f206869676800000000000000000000000000000000000000610720526107008161076001602d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610740528060040161075cfd5b6106e051670de0b6b3a7640000810281670de0b6b3a764000082041861589657905061058051801561589657808204905090508060ff1c615896576105e0518082038281135f83121861589657905090505f811261589657610700526106805161070051808202811583838304141715615896579050905061060051801561589657808204905090506106805180820382811161589657905090506107205260206107206103c2565b6105a05163bbbf46a1610620526105405161064052602435610660526040610620604461063c845afa6102ea573d5f5f3e3d5ffd5b60403d106158965761062090506040816105e05e50610600516105c05110156103855760208061068052600d610620527f4465627420746f6f206869676800000000000000000000000000000000000000610640526106208161068001602d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610660528060040161067cfd5b61060051670de0b6b3a7640000810281670de0b6b3a764000082041861589657905061058051801561589657808204905090506106205260206106205bf35b62aeef8a8118613ead576064361034176158965733610540526105e2565b63f51dd48581186105a157602436103417615896575f5c6001146158965761040b610620613efd565b6106205161072052610720516102805261042661064061441b565b61064060e0816105405e506001546386b301ad610680526060610680600461069c845afa610456573d5f5f3e3d5ffd5b60603d106158965761068090506060816106205e50610540515f8181185f8313021890505f8112615896576106805261056051670de0b6b3a7640000810281670de0b6b3a7640000820418615896579050610560516106805180820182811061589657905090508015615896578082049050905060043580820281158383830414171561589657905090506105e051801561589657808204905090506106a052610620516106a0518082028115838383041417156158965790509050670de0b6b3a7640000810490506106c052610640516106a0518082028115838383041417156158965790509050670de0b6b3a7640000810490506106e052602060206158f05f395f5163d5f8da30610700526106c051610720525f610740526106e051610760526020610700606461071c845afa610592573d5f5f3e3d5ffd5b60203d10615896576107009050f35b6336b2e3e88118613ead573461589657600b5460405260206040f35b63fad3cc4b8118610c9d57608436103417615896576064358060a01c61589657610540525b5f5c6001146158965760015f5d6003546105605261056051610540511861067b576020806105e0526011610580527f4465706f73697420746f207374616b65720000000000000000000000000000006105a052610580816105e001603182825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105c052806004016105dcfd5b6001546105805260206159105f395f516323b872dd6105a052610580516105c052306105e0526024356106005260206105a060646105bc5f855af16106c2573d5f5f3e3d5ffd5b3d6106d957803b1561589657600161062052610703565b3d602081183d6020100218806105a0016105c011615896576105a0518060011c6158965761062052505b610620905051156158965760206159305f395f516323b872dd6105a052336105c052306105e0526004356106005260206105a060646105bc5f855af161074b573d5f5f3e3d5ffd5b3d61076257803b156158965760016106205261078c565b3d602081183d6020100218806105a0016105c011615896576105a0518060011c6158965761062052505b610620905051156158965760206158f05f395f5163865147386105c0526024356105e052600435610600525f6106205261058051610640525f6106605260206105c060a46105dc5f855af16107e3573d5f5f3e3d5ffd5b60203d10615896576105c09050516105a0526108006105e0614d63565b6105e0516105c052600a546105e05261010036610600376105e05115610848576105c0516102805261083361070061441b565b61070060e0816107e05e5060e06107e06106205e5b6105805163f3207723610740526105a05161076052602435610780526040610740604461075c5f855af161087e573d5f5f3e3d5ffd5b60403d106158965761074090506040816107005e5061072051670de0b6b3a7640000810281670de0b6b3a76400008204186158965790506105c0518015615896578082049050905061074052610720516105805163638a1e8d610760526020610760600461077c845afa6108f4573d5f5f3e3d5ffd5b60203d10615896576107609050518060011c905010156109865760208061080052600d6107a0527f4465627420746f6f2068696768000000000000000000000000000000000000006107c0526107a08161080001602d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06107e052806004016107fcfd5b6105e0511561099a5761064051151561099c565b5f5b610a395761074051610600525f6006555f600755610600516105e05180820182811061589657905090506005555f6004556009610560516020525f5260405f205415610a23575f610560517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef6009610560516020525f5260405f2054610760526020610760a35b5f6009610560516020525f5260405f2055610b09565b6106c0516105e052610620516004556106405161076052610740518060ff1c61589657610620518082038281135f83121861589657905090505f8112615896576107405261074051600555610680516007556106c051600a556105605115610ac5576106a0516009610560516020525f5260405f2055610560516040526106e051606052610ac5614dee565b6105e05161074051808202811583838304141715615896579050905061076051801561589657808204905090506105e0518082038281116158965790509050610600525b620f4240610600516105e05180820182811061589657905090501015610ba1576020806107c0526013610760527f52656d61696e64657220746f6f20736d616c6c0000000000000000000000000061078052610760816107c001603382825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06107a052806004016107bcfd5b604435610600511015610c26576020806107c0526008610760527f536c69707061676500000000000000000000000000000000000000000000000061078052610760816107c001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06107a052806004016107bcfd5b6105405160405261060051606052610c3c614ebc565b610c44614f29565b61054051337fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d76004356107605261060051610780526040610760a3662386f26fc1000061018052610c936151e8565b60206106005f5f5df35b637ba3152e8118610cbe573461589657662386f26fc100006102c052612765565b6390b229978118613ead57602436103417615896576004358060011c615896576040525f546060526060513b15610e3757336060518118610d00576001610db9565b60605163f851a440610140526020610140600461015c845afa610d25573d5f5f3e3d5ffd5b3d602081183d602010021880610140016101601161589657610140518060a01c6158965761018052506101809050518118610d61576001610db9565b60605163680c77836101a05260206101a060046101bc845afa610d86573d5f5f3e3d5ffd5b3d602081183d6020100218806101a0016101c011615896576101a0518060a01c615896576101e052506101e09050518118155b9050610eb257602080610280526006610220527f4163636573730000000000000000000000000000000000000000000000000000610240526102208161028001602682825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610260528060040161027cfd5b606051331815610eb25760208060e05260066080527f416363657373000000000000000000000000000000000000000000000000000060a05260808160e001602682825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a060c0528060040160dcfd5b6001546390b2299760805260405160a052803b15615896575f60806024609c5f855af1610ee1573d5f5f3e3d5ffd5b50005b63441a3e708118610f0357604436103417615896573361054052610f28565b630ad58d2f811861170f57606436103417615896576044358060a01c61589657610540525b5f5c6001146158965760015f5d600435610fb4576020806105c0526013610560527f5769746864726177696e67206e6f7468696e670000000000000000000000000061058052610560816105c001603382825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105a052806004016105bcfd5b6003546105605261056051338114610fd25761054051811415610fd4565b5f5b9050611052576020806106005260176105a0527f576974686472617720746f2f66726f6d207374616b65720000000000000000006105c0526105a08161060001603782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105e052806004016105fcfd5b600154639c868ac0610580526020610580600461059c845afa611077573d5f5f3e3d5ffd5b3d602081183d602010021880610580016105a01161589657610580518060011c615896576105c052506105c090505115611148576020806106605260226105e0527f576527726520646561642e2055736520656d657267656e63795f776974686472610600527f6177000000000000000000000000000000000000000000000000000000000000610620526105e08161066001604282825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610640528060040161065cfd5b6001546105805261115a610680614d63565b610680516107805261078051610280526111756106a061441b565b6106a060e0816105a05e5061064051610680526105a0516004556106005160075561068051600a5560043580620f424001620f424081106158965790506106805110156111ca576004356106805118156111cd565b60015b611249576020806107005260136106a0527f52656d61696e64657220746f6f20736d616c6c000000000000000000000000006106c0526106a08161070001603382825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06106e052806004016106fcfd5b610560511561127c57610620516009610560516020525f5260405f2055610560516040526106605160605261127c614dee565b6105a0515f8181185f8313021890505f8112615896576106a0526105805163ac6a2b5d610700526105c051670de0b6b3a7640000810281670de0b6b3a76400008204186158965790506105c0516106a05180820182811061589657905090508015615896578082049050905060043580820281158383830414171561589657905090506106805180156158965780820490509050610720526040610700602461071c5f855af161132e573d5f5f3e3d5ffd5b60403d106158965761070090506040816106c05e5060206158f05f395f516323b872dd61070052610580516107205230610740526106c051610760526020610700606461071c5f855af1611384573d5f5f3e3d5ffd5b3d61139b57803b15615896576001610780526113c5565b3d602081183d602010021880610700016107201161589657610700518060011c6158965761078052505b610780905051156158965760206158f05f395f5163512d6365610720526106c051610740525f610760526106e051610780525f6107a0526020610720608461073c5f855af1611416573d5f5f3e3d5ffd5b60203d1061589657610720905051610700523360405260043560605261143a6153a9565b6105c051610680516004358082038281116158965790509050808202811583838304141715615896579050905061068051801561589657808204905090506005557fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6105a05113611518576105a0516106805160043580820382811161589657905090508060ff1c6158965780820281191515600160ff1b84141517821584848405141716156158965790509050610680518060ff1c61589657801561589657808205600160ff1b8314155f19831415171561589657905090506004555b60243561070051101561159d57602080610780526008610720527f536c697070616765000000000000000000000000000000000000000000000000610740526107208161078001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610760528060040161077cfd5b60206159105f395f5163a9059cbb6107205261058051610740526106e051610760526020610720604461073c5f855af16115d9573d5f5f3e3d5ffd5b3d6115f057803b156158965760016107805261161a565b3d602081183d602010021880610720016107401161589657610720518060011c6158965761078052505b610780905051156158965760206159305f395f5163a9059cbb61072052610540516107405261070051610760526020610720604461073c5f855af1611661573d5f5f3e3d5ffd5b3d61167857803b15615896576001610780526116a2565b3d602081183d602010021880610720016107401161589657610720518060011c6158965761078052505b61078090505115615896576116b5614f29565b3361054051337ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db6107005161072052600435610740526040610720a4662386f26fc10000610180526117056151e8565b60206107005f5f5df35b639c868ac08118613ead5734615896576020600154639c868ac0604052602060406004605c845afa611743573d5f5f3e3d5ffd5b3d602081183d602010021880604001606011615896576040518060011c615896576080525060809050f35b63650dfae28118611b1757602436103417615896575f5c6001146158965761010036610540376001546106405261064051639c868ac0610660526020610660600461067c845afa6117c1573d5f5f3e3d5ffd5b3d602081183d602010021880610660016106801161589657610660518060011c615896576106a052506106a090505161183b576117ff610660613efd565b6106605161076052610760516102805261181a61068061441b565b61068060e0816107805e5060e06107806105605e6106005161054052611843565b600a54610540525b600435670de0b6b3a7640000810281670de0b6b3a7640000820418615896579050610540518015615896578082049050905061066052600161056051121561188b575f611892565b6105805115155b156118e25761066051610580518082028115838383041417156158965790509050610560515f81126158965761058051808201828110615896579050905080156158965780820490509050610660525b6106405163e0f038e76106a05260206106a060046106bc845afa611908573d5f5f3e3d5ffd5b60203d10615896576106a0905051610660518082028115838383041417156158965790509050670de0b6b3a7640000810490506106805261064051630b576c966106c05260206106c060046106dc845afa611965573d5f5f3e3d5ffd5b60203d10615896576106c0905051610660518082028115838383041417156158965790509050604052670de0b6b3a76400006060526119a5610700615416565b610700518060ff1c615896576106a05260206158f05f395f516318160ddd6106e05260206106e060046106fc845afa6119e0573d5f5f3e3d5ffd5b60203d10615896576106e09050516106c05260206158f05f395f51634903b0d1610720525f610740526020610720602461073c845afa611a22573d5f5f3e3d5ffd5b60203d10615896576107209050516106e05260206158f05f395f51634903b0d1610760526001610780526020610760602461077c845afa611a65573d5f5f3e3d5ffd5b60203d106158965761076090505161070052610680516106e05180820281158383830414171561589657905090506106c0518015615896578082049050905061072052610680516107005180820281158383830414171561589657905090506106c051801561589657808204905090506107405260406107206106e05e61070051610720526106e0518060ff1c615896576106a0518082038281135f8312186158965790509050610740526040610720f35b63ec46bf8f8118611b3b576024361034176158965733610540523361056052611c3b565b634189617d8118613ead57602436103417615896575f5c6001146158965760015f5d611b65615084565b600154631aa02d59610180526004356101a052803b15615896575f610180602461019c5f855af1611b98573d5f5f3e3d5ffd5b505f5f5d005b63b78be8028118611bcc57604436103417615896576024358060a01c61589657610540523361056052611c3b565b6370a082318118613ead57602436103417615896576004358060a01c6158965760405260096040516020525f5260405f205460605260206060f35b63d5569431811861274857606436103417615896576024358060a01c61589657610540526044358060a01c61589657610560525b5f5c6001146158965760015f5d6003546105805261058051610560518114611c695761054051811415611c6b565b5f5b9050611ce9576020806106205260176105c0527f576974686472617720746f2f66726f6d207374616b65720000000000000000006105e0526105c08161062001603782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610600528060040161061cfd5b610100366105a0376001546106a0526106a051639c868ac06106e05260206106e060046106fc845afa611d1e573d5f5f3e3d5ffd5b3d602081183d6020100218806106e00161070011615896576106e0518060011c6158965761072052506107209050516106c0526106c051611e6d5733610560511815611ddc5760208061074052600a6106e0527f4e6f74206b696c6c656400000000000000000000000000000000000000000000610700526106e08161074001602a82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610720528060040161073cfd5b611de76106e0614d63565b6106e0516107e0526107e05161028052611e0261070061441b565b61070060e0816108005e5060e06108006105c05e610660516105a0526105c0516004556105e051600555610620516007556105a051600a5561058051156120fc57610640516009610580516020525f5260405f205561058051604052610680516060526120fc614dee565b5f546106e0526106e0513b15611fe1576106e05163680c7783610700526020610700600461071c845afa611ea3573d5f5f3e3d5ffd5b3d602081183d602010021880610700016107201161589657610700518060a01c6158965761074052506107409050513318611f5e57610560516105405118156120f4576020806107c0526008610760527f726563656976657200000000000000000000000000000000000000000000000061078052610760816107c001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06107a052806004016107bcfd5b336105605118156120f457602080610760526005610700527f6f776e6572000000000000000000000000000000000000000000000000000000610720526107008161076001602582825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610740528060040161075cfd5b6106e051331861207157610560516105405118156120f457602080610760526008610700527f7265636569766572000000000000000000000000000000000000000000000000610720526107008161076001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610740528060040161075cfd5b336105605118156120f457602080610760526005610700527f6f776e6572000000000000000000000000000000000000000000000000000000610720526107008161076001602582825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610740528060040161075cfd5b600a546105a0525b60043580620f424001620f424081106158965790506105a0511015612129576004356105a051181561212c565b60015b6121a8576020806107405260136106e0527f52656d61696e64657220746f6f20736d616c6c00000000000000000000000000610700526106e08161074001603382825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610720528060040161073cfd5b600435670de0b6b3a7640000810281670de0b6b3a76400008204186158965790506105a051801561589657808204905090506106e0526106e0518060ff1c615896576107005260016105c0511215612200575f612207565b6105e05115155b15612257576106e0516105e05180820281158383830414171561589657905090506105c0515f8112615896576105e0518082018281106158965790509050801561589657808204905090506106e0525b6106a05163ac6a2b5d610760526106e051610780526040610760602461077c5f855af1612286573d5f5f3e3d5ffd5b60403d106158965761076090506040816107205e5060206158f05f395f516323b872dd610760526106a05161078052306107a052610720516107c0526020610760606461077c5f855af16122dc573d5f5f3e3d5ffd5b3d6122f357803b156158965760016107e05261231d565b3d602081183d602010021880610760016107801161589657610760518060011c615896576107e052505b6107e0905051156158965760206158f05f395f51635b36389c6107a052610720516107c0526040366107e03760406107a060646107bc5f855af1612363573d5f5f3e3d5ffd5b60403d10615896576107a090506040816107605e50610760518060ff1c61589657610740518060ff1c615896578082038281135f83121861589657905090506107a052610560516040526004356060526123bb6153a9565b6005546105a051600435808203828111615896579050905080820281158383830414171561589657905090506105a051801561589657808204905090506005557fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600454131561242e576106c051612431565b60015b15612490576004546107005180670de0b6b3a764000003670de0b6b3a764000081135f83121861589657905080820281191515600160ff1b84141517821584848405141716156158965790509050670de0b6b3a7640000810590506004555b60016107a0511215612585577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6107a051136126155760206159105f395f516323b872dd6107c052336107e05230610800526107a0517f80000000000000000000000000000000000000000000000000000000000000008114615896575f035f8112615896576108205260206107c060646107dc5f855af1612534573d5f5f3e3d5ffd5b3d61254b57803b1561589657600161084052612575565b3d602081183d6020100218806107c0016107e011615896576107c0518060011c6158965761084052505b6108409050511561589657612615565b60206159105f395f5163a9059cbb6107c052610540516107e0526107a0515f8112615896576108005260206107c060446107dc5f855af16125c8573d5f5f3e3d5ffd5b3d6125df57803b1561589657600161082052612609565b3d602081183d6020100218806107c0016107e011615896576107c0518060011c6158965761082052505b61082090505115615896575b60206159105f395f5163a9059cbb6107c0526106a0516107e052610740516108005260206107c060446107dc5f855af1612651573d5f5f3e3d5ffd5b3d61266857803b1561589657600161082052612692565b3d602081183d6020100218806107c0016107e011615896576107c0518060011c6158965761082052505b610820905051156158965760206159305f395f5163a9059cbb6107c052610540516107e052610780516108005260206107c060446107dc5f855af16126d9573d5f5f3e3d5ffd5b3d6126f057803b156158965760016108205261271a565b3d602081183d6020100218806107c0016107e011615896576107c0518060011c6158965761082052505b61082090505115615896576106c05161273557612735614f29565b60406107806107c05e60406107c05f5f5df35b63e3e4bbbb8118613ead57602436103417615896576004356102c0525b5f5c6001146158965760015f5d6102c051610180526127826151e8565b5f5f5d005b638fa57492811861288a57346158965760035461054052600154639c868ac0610580526020610580600461059c845afa6127c3573d5f5f3e3d5ffd5b3d602081183d602010021880610580016105a01161589657610580518060011c615896576105c052506105c09050516105605261054051331861280a57610560511561280c565b5f5b156128885761281c610660614d63565b6106605161076052610760516102805261283761068061441b565b61068060e0816105805e50610580516004556105a0516005556105e05160075561062051600a55610600516009610540516020525f5260405f20556105405160405261064051606052612888614dee565b005b636d8934498118613ead573461589657600c5460405260206040f35b6399530b068118613ead5734615896576128c1610620613efd565b610620516107205261072051610280526128dc61064061441b565b61064060e0816105405e506105e05161290657670de0b6b3a7640000610620526020610620612943565b61056051670de0b6b3a7640000810281670de0b6b3a76400008204186158965790506105e051801561589657808204905090506106205260206106205bf35b636831c45b8118612bd457602436103417615896576004358060a01c61589657610180525f5c6001146158965760015f5d61297e615084565b600154156129fe5760208061020052600b6101a0527f416c7265616479207365740000000000000000000000000000000000000000006101c0526101a08161020001602b82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101e052806004016101fcfd5b60206159105f395f51610180516393a397766101a05260206101a060046101bc845afa612a2d573d5f5f3e3d5ffd5b3d602081183d6020100218806101a0016101c011615896576101a0518060a01c615896576101e052506101e0905051186158965760206158f05f395f51610180516324bbab8b6101a05260206101a060046101bc845afa612a90573d5f5f3e3d5ffd5b3d602081183d6020100218806101a0016101c011615896576101a0518060a01c615896576101e052506101e09050511861589657306101805163a3cd68056101a05260206101a060046101bc845afa612aeb573d5f5f3e3d5ffd5b3d602081183d6020100218806101a0016101c011615896576101a0518060a01c615896576101e052506101e0905051186158965761018051600155610180516378f5e9f06101a05260206101a060046101bc845afa612b4c573d5f5f3e3d5ffd5b3d602081183d6020100218806101a0016101c011615896576101a0518060a01c615896576101e052506101e090505163858051e2610200526020610200600461021c845afa612b9d573d5f5f3e3d5ffd5b3d602081183d602010021880610200016102201161589657610200518060a01c6158965761024052506102409050516002555f5f5d005b63313ce5678118613ead573461589657601260405260206040f35b63e9333fab8118612e0957602436103417615896576004358060a01c61589657610180525f5c6001146158965760015f5d612c28615084565b610180513b15612dcf576101805163f851a4406101c05260206101c060046101dc845afa612c58573d5f5f3e3d5ffd5b3d602081183d6020100218806101c0016101e011615896576101c0518060a01c6158965761020052506102009050516101a0526101805163680c77836101c05260206101c060046101dc845afa612cb1573d5f5f3e3d5ffd5b3d602081183d6020100218806101c0016101e011615896576101c0518060a01c6158965761020052506102009050516101a0526101805163cab4d3db6101c05260206101c060046101dc845afa612d0a573d5f5f3e3d5ffd5b3d602081183d6020100218806101c0016101e011615896576101c0518060a01c6158965761020052506102009050516101a0526101805163d8b9a0186101c05260206101c060046101dc845afa612d63573d5f5f3e3d5ffd5b3d602081183d6020100218806101c0016101e011615896576101c0518060a01c6158965761020052506102009050516101a05261018051638c5710c96101e05260206101e060046101fc845afa612dbc573d5f5f3e3d5ffd5b60203d10615896576101e09050516101c0525b610180515f557f5a272403b402d892977df56625f4164ccaf70ca3863991c43ecfe76a6905b0a1610180516101a05260206101a0a15f5f5d005b6323b872dd8118613ead57606436103417615896576004358060a01c615896576108c0526024358060a01c615896576108e05260086108c0516020525f5260405f2080336020525f5260405f20905054610900527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6109005114612eb2576108c05160405233606052610900516044358082038281116158965790509050608052612eb2615841565b60406108c06105405e60443561058052612eca6154b8565b6001610920526020610920f35b63d4387a998118613ead57602436103417615896575f5c6001146158965760015f5d612f01615084565b60015463d4387a99610180526004356101a0526020610180602461019c5f855af1612f2e573d5f5f3e3d5ffd5b60203d106158965761018050505f5f5d005b6316126c508118612f7a5734615896577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61018052612fef565b63dd62ed3e8118613ead57604436103417615896576004358060a01c615896576040526024358060a01c6158965760605260086040516020525f5260405f20806060516020525f5260405f2090505460805260206080f35b63e5ef2aae8118613ead5760243610341761589657600435610180525b5f5c6001146158965760015f5d5f546101a052610180516101c052600c546101e052610180511961302657600b546101c052613036565b61302e615084565b61018051600b555b600154630469c15761020052803b15615896575f610200600461021c5f855af1613062573d5f5f3e3d5ffd5b506101e0516101c051116132fd576101e0516101c05110156133a6576001546378f5e9f0610220526020610220600461023c845afa6130a3573d5f5f3e3d5ffd5b3d602081183d602010021880610220016102401161589657610220518060a01c61589657610260525061026090505163ceb7f759610280526020610280600461029c5f855af16130f5573d5f5f3e3d5ffd5b60203d1061589657610280905051610200526102005160015463e0f038e7610220526020610220600461023c845afa613130573d5f5f3e3d5ffd5b60203d10615896576102209050518082028115838383041417156158965790509050670de0b6b3a7640000810490506101c05110156131e1576020806102c0526012610260527f4e6f7420656e6f75676820737461626c6573000000000000000000000000000061028052610260816102c001603282825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06102a052806004016102bcfd5b6101e0516101c051808203828111615896579050905060206159105f395f516370a0823161024052600154610260526020610240602461025c845afa613229573d5f5f3e3d5ffd5b60203d106158965761024090505180828118828410021890509050610220526101e0516102205180820382811161589657905090506101e05260206159105f395f516323b872dd61024052600154610260526101a05161028052610220516102a0526020610240606461025c5f855af16132a5573d5f5f3e3d5ffd5b3d6132bc57803b156158965760016102c0526132e6565b3d602081183d602010021880610240016102601161589657610240518060011c615896576102c052505b6102c090505115615896576101e051600c556133a6565b60206159105f395f516323b872dd610200526101a05161022052600154610240526101c0516101e0518082038281116158965790509050610260526020610200606461021c5f855af1613352573d5f5f3e3d5ffd5b3d61336957803b1561589657600161028052613393565b3d602081183d602010021880610200016102201161589657610200518060011c6158965761028052505b61028090505115615896576101c051600c555b6101a0517f6f16a992fba7b966942bb6064775e154f99de09fce962940d4e284c5c913286860406101c06102005e6040610200a25f5f5d005b6330c54085811861389c5734615896575f5c6001146158965760015f5d5f5461054052610540513b613483576020806105c052600c610560527f4e65656420666163746f7279000000000000000000000000000000000000000061058052610560816105c001602c82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105a052806004016105bcfd5b600154639c868ac0610560526020610560600461057c845afa6134a8573d5f5f3e3d5ffd5b3d602081183d602010021880610560016105801161589657610560518060011c615896576105a052506105a090505115613554576020806106205260066105c0527f4b696c6c656400000000000000000000000000000000000000000000000000006105e0526105c08161062001602682825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610600528060040161061cfd5b600154630469c15761056052803b15615896575f610560600461057c5f855af1613580573d5f5f3e3d5ffd5b506105405163cab4d3db610580526020610580600461059c845afa6135a7573d5f5f3e3d5ffd5b3d602081183d602010021880610580016105a01161589657610580518060a01c615896576105c052506105c0905051610560526105605161365a576020806105e052600f610580527f4e6f206665655f726563656976657200000000000000000000000000000000006105a052610580816105e001602f82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105c052806004016105dcfd5b600354610580526105805161056051186136e6576020806106005260136105a0527f5374616b65723d6665655f7265636569766572000000000000000000000000006105c0526105a08161060001603382825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105e052806004016105fcfd5b6136f1610680614d63565b6106805161078052610780516102805261370c6106a061441b565b6106a060e0816105a05e505f6105a051121561379a576020806106e052601c610680527f4c6f7373206d6164652061646d696e20666565206e65676174697665000000006106a052610680816106e001603c82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06106c052806004016106dcfd5b61064051600a556105c0516105a0515f811261589657808201828110615896579050905061068052610640516106805180820281158383830414171561589657905090506105c051801561589657808204905090506106405180820382811161589657905090506106a052610560516040526106a05160605261381b614ebc565b610680516005555f60045561060051600755610580511561386057610620516009610580516020525f5260405f20556105805160405261066051606052613860614dee565b7f78b4892921a904766a871a893b75e9461080a308419f358353f73739b29c7fc4610560516106c0526106a0516106e05260406106c0a15f5f5d005b6393d9d94c8118613ead57346158965760206158f060403960206040f35b63464d700b8118613acb57602436103417615896576004358060a01c615896576108c0525f5c6001146158965760015f5d6003541561396b576020806109405260126108e0527f5374616b657220616c7265616479207365740000000000000000000000000000610900526108e08161094001603282825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610920528060040161093cfd5b6108c051156158965761397c615084565b60096108c0516020525f5260405f20546108e0526108e05115613a95575f5463cab4d3db610920526020610920600461093c845afa6139bd573d5f5f3e3d5ffd5b3d602081183d602010021880610920016109401161589657610920518060a01c61589657610960525061096090505161090052610900516108c05118613a7557602080610980526013610920527f5374616b65723d6665655f726563656976657200000000000000000000000000610940526109208161098001603382825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610960528060040161097cfd5b6108c0516105405261090051610560526108e05161058052613a956154b8565b6108c0516003556108c0517f6901da73a3480b0bbd7deb64f259ff29a76ce811c7a3e5cf495f0e1eb4fb4dfb5f610900a25f5f5d005b63d70620058118613ae9573461589657602061593060403960206040f35b631a6865028118613ead57346158965760045460405260055460605260065460805260075460a05260806040f35b6395d89b418118613ead5734615896576020806101a0525f60036040527f79622d0000000000000000000000000000000000000000000000000000000000606052604080516020820183610160018151815250508083019250505060206159305f395f516395d89b41608052606060806004609c845afa613b9a573d5f5f3e3d5ffd5b3d606081183d60601002188060800160a0116158965760806080516080011061589657608051608001805182608001825160200183011161589657601d81116158965750603d816101005e5050610100905080516020820183610160018151815250508083019250505080610140526101409050816101a001604082825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506101a0f35b6306fdde038118613ead5734615896576020806101c0525f601a6040527f5969656c64204261736973206c697175696469747920666f7220000000000000606052604080516020820183610160018151815250508083019250505060206159305f395f516395d89b41608052606060806004609c845afa613cc7573d5f5f3e3d5ffd5b3d606081183d60601002188060800160a0116158965760806080516080011061589657608051608001805182608001825160200183011161589657601d81116158965750603d816101005e5050610100905080516020820183610160018151815250508083019250505080610140526101409050816101c001605782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506101c0f35b63a9059cbb8118613dbe57604436103417615896576004358060a01c615896576108c05233610540526108c0516105605260243561058052613db16154b8565b60016108e05260206108e0f35b6318160ddd8118613ead573461589657600a5460405260206040f35b63095ea7b38118613ead57604436103417615896576004358060a01c6158965760c0523360405260c051606052602435608052613e15615841565b600160e052602060e0f35b6393a397768118613ead573461589657602061591060403960206040f35b63f851a4408118613ead5734615896575f5460405260206040f35b632a9439458118613ead57346158965760015460405260206040f35b63f5e34bfa8118613ead57346158965760025460405260206040f35b635ebaf1db8118613ead57346158965760035460405260206040f35b5f5ffd5b5f546040526040513b15613ef657604051638c5710c9606052602060606004607c845afa613ee1573d5f5f3e3d5ffd5b60203d10615896576060905051815250613efb565b5f8152505b565b60206158f05f395f516386fc88d3604052602060406004605c845afa613f25573d5f5f3e3d5ffd5b60203d1061589657604090505160025463a035b1fe608052602060806004609c845afa613f54573d5f5f3e3d5ffd5b60203d106158965760809050518082028115838383041417156158965790509050670de0b6b3a764000081049050815250565b6080516140035760208061012052601e60c0527f6d6174683a206d756c5f646976206469766973696f6e206279207a65726f000060e05260c08161012001603e82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610100528060040161011cfd5b6040516060517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8183099050905060c0526060516040510260e0525f6101005260e05160c0511061405e5760e05160c051036101005261406d565b600160e05160c0510303610100525b610100516140c45760a051614082575f61409e565b6040516060516080518015615896578082840990509050905015155b156140b557600160805160e05104018152506142b3565b60805160e051048152506142b3565b610100516080511161414857602080610180526016610120527f6d6174683a206d756c5f646976206f766572666c6f7700000000000000000000610140526101208161018001603682825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610160528060040161017cfd5b604051606051608051801561589657808284099050905090506101205260e05161012051111561417e5760016101005103610100525b6101205160e0510360e0526080516080515f0316610140526101405160805104610160526101405160e0510460e052600161014051610140515f0304016101405261014051610100510260e0511760e05260026101605160030218610180526101805161016051026002036101805102610180526101805161016051026002036101805102610180526101805161016051026002036101805102610180526101805161016051026002036101805102610180526101805161016051026002036101805102610180526101805161016051026002036101805102610180526101805160e051026101a05260a051614274575f614290565b6040516060516080518015615896578082840990509050905015155b156142ab576101a051600181018181106158965790506101a0525b6101a0518152505b565b610220516142c6575f815250614419565b6101e0517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8113156142f85780614305565b805f03811461589657805f035b90505f811261589657604052610200517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8113156143435780614350565b805f03811461589657805f035b90505f811261589657606052610220517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81131561438e578061439b565b805f03811461589657805f035b90505f8112615896576080525f60a0526143b6610260613f87565b610260518060ff1c61589657610240525f61022051125f61020051125f6101e0511214151461441157610240517f80000000000000000000000000000000000000000000000000000000000000008114615896575f03610240525b610240518152505b565b6004546102a0526005546102c0526006546102e05260075461030052600354610320525f6103405261032051156144685760096003546020525f5260405f20548060ff1c61589657610340525b600a548060ff1c61589657610360526144826103a0613eb1565b6103a05180670de0b6b3a764000003670de0b6b3a76400008111615896579050610340516ec097ce7bc90715b34b9f10000000008102816ec097ce7bc90715b34b9f100000000082051861589657905061036051801561589657808205600160ff1b8314155f1983141517156158965790509050806ec097ce7bc90715b34b9f1000000000036ec097ce7bc90715b34b9f100000000081135f8312186158965790505f8112615896578060b5710100000000000000000000000000000000008210614554578160801c91508060401b90505b69010000000000000000008210614572578160401c91508060201b90505b65010000000000821061458c578160201c91508060101b90505b630100000082106145a4578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c905080830480828118828410021890509050905090508082028115838383041417156158965790509050670de0b6b3a76400008104905080670de0b6b3a764000003670de0b6b3a764000081116158965790508060ff1c6158965761038052600154635ef7feab6103c05260406103c060046103dc845afa614679573d5f5f3e3d5ffd5b60403d10615896576103c0905060208101905051670de0b6b3a7640000810281670de0b6b3a764000082041861589657905061028051801561589657808204905090508060ff1c615896576103a0526102c0518060ff1c615896576103c0526103a0516103c0516102a0518082018281125f83121861589657905090508082038281135f83121861589657905090506103e052610300518060ff1c61589657610400526102e0518060ff1c61589657610420525f6104405261042051610400518082038281135f83121861589657905090505f8181185f83130218905061046052662386f26fc100006103405112156147bf576103e0516103805180670de0b6b3a764000003670de0b6b3a764000081135f83121861589657905080820281191515600160ff1b84141517821584848405141716156158965790509050610440526148f2565b60016103e05112156147f6576103e051670de0b6b3a7640000810281670de0b6b3a7640000820518615896579050610440526148f2565b6103e051610460516103605180820281191515600160ff1b8414151782158484840514171615615896579050905061034051801561589657808205600160ff1b8314155f1983141517156158965790509050808281188284120218905090506104805261048051670de0b6b3a7640000810281670de0b6b3a76400008205186158965790506103e051610480518082038281135f83121861589657905090506103805180670de0b6b3a764000003670de0b6b3a764000081135f83121861589657905080820281191515600160ff1b841415178215848484051417161561589657905090508082018281125f8312186158965790509050610440525b6102a0516103e05161044051670de0b6b3a7640000810590508082038281135f83121861589657905090508082018281125f83121861589657905090506102a052610440516101e052610340516102005261036051610220526149566104a06142b5565b6104a05161048052600161044051126149a1576104805161046051670de0b6b3a7640000810281670de0b6b3a764000082051861589657905080828118828412021890509050610480525b6103c051670de0b6b3a7640000810281670de0b6b3a7640000820518615896579050610440518082018281125f83121861589657905090505f8181185f8313021890506104a05261040051670de0b6b3a7640000810281670de0b6b3a7640000820518615896579050610480518082018281125f83121861589657905090505f8181185f8313021890506104c0526104a0516104c0518082038281135f83121861589657905090506104e0526104a0516101e05261034051610200526104e05161022052614a706105006142b5565b610500516104c0516101e05261036051610200526104e05161022052614a976105206142b5565b610520518082038281135f83121861589657905090506104e0526103e0516103605180820281191515600160ff1b841415178215848484051417161561589657905090506103c0516103e0518082018281125f831218615896579050905060018101818112615896579050801561589657808205600160ff1b8314155f19831415171561589657905090506103805180670de0b6b3a764000003670de0b6b3a764000081135f83121861589657905080820281191515600160ff1b84141517821584848405141716156158965790509050655af3107a4000810590507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811315614ba15780614bae565b805f03811461589657805f035b90506105005260016103405112614be6576104e0516103405160018103818113615896579050808281188284120218905090506104e0525b60016103605112614c18576104e0516103605160018103818113615896579050808281188284120218905090506104e0525b5f6104e0511215614c6a576104e051610500517f80000000000000000000000000000000000000000000000000000000000000008114615896575f03808281188284130218905090506104e052614c84565b6104e05161050051808281188284120218905090506104e0525b69021e19e0c9bab23fffff6104a0516104c0518082038281135f831218615896579050905013614cc2576104e0515f8181185f8313021890506104e0525b6102a05181526104a051670de0b6b3a7640000810590505f81126158965760208201526102e05160408201526104c051670de0b6b3a7640000810590505f8112615896576060820152610340516104e0518082038281135f83121861589657905090505f8112615896576080820152610360516104e0518082038281135f83121861589657905090505f81126158965760a08201526104e05160c082015250565b60206158f05f395f516386fc88d3604052602060406004605c845afa614d8b573d5f5f3e3d5ffd5b60203d1061589657604090505160025463ceb7f759608052602060806004609c5f855af1614dbb573d5f5f3e3d5ffd5b60203d106158965760809050518082028115838383041417156158965790509050670de0b6b3a764000081049050815250565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60605113614e78576040515f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef6060517f80000000000000000000000000000000000000000000000000000000000000008114615896575f035f81126158965760805260206080a35b600160605112614eba575f6040517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef6060515f81126158965760805260206080a35b565b60096040516020525f5260405f2080546060518082018281106158965790509050815550600a546060518082018281106158965790509050600a556040515f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60605160805260206080a3565b60035460405260405115615082575f546060526060513b156150825760605163d8b9a01860a052602060a0600460bc845afa614f67573d5f5f3e3d5ffd5b3d602081183d60201002188060a00160c0116158965760a0518060a01c6158965760e0525060e090505160805260805115615082576080515a63a972985e60c452600460405160e45260200160c05260c0505f5f60c05160e05f8686f19050905060a05260a0516150825762030d405a10156150525760208061012052600360c0527f474153000000000000000000000000000000000000000000000000000000000060e05260c08161012001602382825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610100528060040161011cfd5b60805163a972985e60c452600460405160e45260200160c05260c0505f5f60c05160e05f8562030d40f1905060a0525b565b5f546040526040513b1561516b5760405133186150a25760016150f2565b60405163f851a440606052602060606004607c845afa6150c4573d5f5f3e3d5ffd5b3d602081183d602010021880606001608011615896576060518060a01c6158965760a0525060a09050513318155b6151e65760208061012052600660c0527f416363657373000000000000000000000000000000000000000000000000000060e05260c08161012001602682825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610100528060040161011cfd5b6040513318156151e65760208060c05260066060527f416363657373000000000000000000000000000000000000000000000000000060805260608160c001602682825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a060a0528060040160bcfd5b565b6001546101a052662386f26fc10001610180511061520857615208615084565b6101a0513314615247576101a051631e0cfcef6101c05260206101c060046101dc5f855af1615239573d5f5f3e3d5ffd5b60203d10615896576101c050505b60206159105f395f516370a082316101e052306102005260206101e060246101fc845afa615277573d5f5f3e3d5ffd5b60203d10615896576101e09050516101c0526101c051156153a7576101805180670de0b6b3a764000003670de0b6b3a764000081116158965790506101c051808202811583838304141715615896579050905060206158f05f395f516354f0f7d5610200526020610200600461021c845afa6152f5573d5f5f3e3d5ffd5b60203d1061589657610200905051801561589657808204905090506101e05260206158f05f395f516386514738610200526101c051610220525f610240526101e051610260525f6102805260016102a052602061020060a461021c5f855af1615360573d5f5f3e3d5ffd5b60203d10615896576102005050337f5bdcb9afddbd4bbebe8e11112e8b8ba0008f38b107b6c8d4bed13e9fb40acb1c60406101c06102005e61018051610240526060610200a25b565b60096040516020525f5260405f2080546060518082038281116158965790509050815550600a546060518082038281116158965790509050600a555f6040517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60605160805260206080a3565b60605161548e5760208060e052601f6080527f6d6174683a206365696c5f646976206469766973696f6e206279207a65726f0060a05260808160e001603f82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a060c0528060040160dcfd5b60405161549b575f6154b3565b60016060516040516001810381811161589657905004015b815250565b610560513081146154cb578015156154cd565b5f5b905015615896576003546105a0526105a05115615508576105a0516105405181186154f9576001615501565b610560518118155b905061550a565b5f5b6105c0526105c05115615799576105605161054051146158965760e0366105e0376105a051331861553c576001615591565b600154639c868ac06106c05260206106c060046106dc845afa615561573d5f5f3e3d5ffd5b3d602081183d6020100218806106c0016106e011615896576106c0518060011c6158965761070052506107009050515b61560e576155a06106c0614d63565b6106c0516107c0526107c051610280526155bb6106e061441b565b6106e060e0816107e05e5060e06107e06105e05e6105e0516004556106005160055561068051600a556106605160096105a0516020525f5260405f20556105a0516040526106a05160605261563f614dee565b600654610620526007546106405260055461060052600a546106805260096105a0516020525f5260405f2054610660525b6105a05161054051186156bf57610640516106805161060051610580518082028115838383041417156158965790509050048082038281116158965790509050610640526106605161062051610660516105805180820382811161589657905090508082028115838383041417156158965790509050046106205261578a565b6105a051610560511861578a576106005161058051808202811583838304141715615896579050905061068051801561589657808204905090506106c052610640516106c0518082018281106158965790509050610640526402540be40161066051101561574657610620516106c05180820182811061589657905090506106205261578a565b610620516106605161058051808201828110615896579050905080820281158383830414171561589657905090506106605180156158965780820490509050610620525b61064051600755610620516006555b6009610540516020525f5260405f2080546105805180820382811161589657905090508155506009610560516020525f5260405f2080546105805180820182811061589657905090508155506105c0516157f3575f6157fb565b6105a0513314155b1561580857615808614f29565b61056051610540517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef610580516105e05260206105e0a3565b60805160086040516020525f5260405f20806060516020525f5260405f209050556060516040517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560805160a052602060a0a3565b5f80fd3ead3ead176e3ead3ead3d711b9e3c4438ba29453ead3e3e00383ead3ead03e205bd3ead2f402bef3dda0ee433df3e203ead3ead3ead1c072ed73b173e912fd227873ead3ead3ead3ead3e7528a63e593ead3ead0018000000000000000000000000d9ff8396554a0d18b2cfbec53e1979b7ecce8373000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c599","optimization_enabled":true,"verified_twin_address_hash":null,"is_verified":true,"compiler_settings":{"search_paths":[".venv/lib/pypy3.11/site-packages","."]},"optimization_runs":null,"sourcify_repo_url":null,"decoded_constructor_args":[["0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",{"name":"asset_token","type":"address"}],["0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E",{"name":"stablecoin","type":"address"}],["0xD9FF8396554A0d18B2CFbeC53e1979b7ecCe8373",{"name":"cryptopool","type":"address"}],["0x370a449FeBb9411c95bf897021377fe0B7D100c0",{"name":"admin","type":"address"}]],"compiler_version":"0.4.3+commit.bff19ea2","is_verified_via_verifier_alliance":true,"verified_at":"2025-10-14T11:12:38.994368Z","implementations":[],"proxy_type":null,"external_libraries":[],"creation_bytecode":"0x61593051503461034e576020615c7a5f395f518060a01c61034e576040526020615c9a5f395f518060a01c61034e576060526020615cba5f395f518060a01c61034e576080526020615cda5f395f518060a01c61034e5760a052606051615910526080516158f0526040516159305260a0515f5560405163095ea7b360c05260805160e0527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61010052602060c0604460dc5f855af16100c1573d5f5f3e3d5ffd5b3d6100d857803b1561034e576001610120526100ff565b3d602081183d60201002188060c00160e01161034e5760c0518060011c61034e5761012052505b6101209050511561034e5760605163095ea7b360c05260805160e0527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61010052602060c0604460dc5f855af1610158573d5f5f3e3d5ffd5b3d61016f57803b1561034e57600161012052610196565b3d602081183d60201002188060c00160e01161034e5760c0518060011c61034e5761012052505b6101209050511561034e5760605160805163c661065760c0525f60e052602060c0602460dc845afa6101ca573d5f5f3e3d5ffd5b3d602081183d60201002188060c00160e01161034e5760c0518060a01c61034e5761010052506101009050511861034e5760405160805163c661065760c052600160e052602060c0602460dc845afa610225573d5f5f3e3d5ffd5b3d602081183d60201002188060c00160e01161034e5760c0518060a01c61034e5761010052506101009050511861034e5760403660c0376080515a63c6610657610124526004600261014452602001610120526101205060206101a0610120516101408585fa905090506101c0523d602081183d6020100218610180526101806040816101e05e506101c05160c05260406101e060e05e60c0511561033c57602080610180526003610120527f4e3e320000000000000000000000000000000000000000000000000000000000610140526101208161018001602382825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610160528060040161017cfd5b6158f061035261000039615950610000f35b5f80fd5f3560e01c6002602b820660011b61589a01601e395f51565b638c5710c98118613ead573461589657602061003460a0613eb1565b60a0f35b633911670481186103c457604436103417615896575f5c6001146158965760206158f05f395f5163ed8e84f361056052602435610580526004356105a05260016105c0526020610560606461057c845afa610095573d5f5f3e3d5ffd5b60203d106158965761056090505161054052600a54610560526100b96105a0613efd565b6105a051610580526001546105a0526105a05163638a1e8d6105e05260206105e060046105fc845afa6100ee573d5f5f3e3d5ffd5b60203d10615896576105e09050518060011c90506105c05261056051156102b55761058051610280526101226106c061441b565b6106c060e0816105e05e5061060051156102b5576105a05163059eaa78610700526105405161072052602435610740526001610760526040610700606461071c845afa610171573d5f5f3e3d5ffd5b60403d106158965761070090506040816106c05e506106e0516105c051101561020c5760208061076052600d610700527f4465627420746f6f206869676800000000000000000000000000000000000000610720526107008161076001602d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610740528060040161075cfd5b6106e051670de0b6b3a7640000810281670de0b6b3a764000082041861589657905061058051801561589657808204905090508060ff1c615896576105e0518082038281135f83121861589657905090505f811261589657610700526106805161070051808202811583838304141715615896579050905061060051801561589657808204905090506106805180820382811161589657905090506107205260206107206103c2565b6105a05163bbbf46a1610620526105405161064052602435610660526040610620604461063c845afa6102ea573d5f5f3e3d5ffd5b60403d106158965761062090506040816105e05e50610600516105c05110156103855760208061068052600d610620527f4465627420746f6f206869676800000000000000000000000000000000000000610640526106208161068001602d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610660528060040161067cfd5b61060051670de0b6b3a7640000810281670de0b6b3a764000082041861589657905061058051801561589657808204905090506106205260206106205bf35b62aeef8a8118613ead576064361034176158965733610540526105e2565b63f51dd48581186105a157602436103417615896575f5c6001146158965761040b610620613efd565b6106205161072052610720516102805261042661064061441b565b61064060e0816105405e506001546386b301ad610680526060610680600461069c845afa610456573d5f5f3e3d5ffd5b60603d106158965761068090506060816106205e50610540515f8181185f8313021890505f8112615896576106805261056051670de0b6b3a7640000810281670de0b6b3a7640000820418615896579050610560516106805180820182811061589657905090508015615896578082049050905060043580820281158383830414171561589657905090506105e051801561589657808204905090506106a052610620516106a0518082028115838383041417156158965790509050670de0b6b3a7640000810490506106c052610640516106a0518082028115838383041417156158965790509050670de0b6b3a7640000810490506106e052602060206158f05f395f5163d5f8da30610700526106c051610720525f610740526106e051610760526020610700606461071c845afa610592573d5f5f3e3d5ffd5b60203d10615896576107009050f35b6336b2e3e88118613ead573461589657600b5460405260206040f35b63fad3cc4b8118610c9d57608436103417615896576064358060a01c61589657610540525b5f5c6001146158965760015f5d6003546105605261056051610540511861067b576020806105e0526011610580527f4465706f73697420746f207374616b65720000000000000000000000000000006105a052610580816105e001603182825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105c052806004016105dcfd5b6001546105805260206159105f395f516323b872dd6105a052610580516105c052306105e0526024356106005260206105a060646105bc5f855af16106c2573d5f5f3e3d5ffd5b3d6106d957803b1561589657600161062052610703565b3d602081183d6020100218806105a0016105c011615896576105a0518060011c6158965761062052505b610620905051156158965760206159305f395f516323b872dd6105a052336105c052306105e0526004356106005260206105a060646105bc5f855af161074b573d5f5f3e3d5ffd5b3d61076257803b156158965760016106205261078c565b3d602081183d6020100218806105a0016105c011615896576105a0518060011c6158965761062052505b610620905051156158965760206158f05f395f5163865147386105c0526024356105e052600435610600525f6106205261058051610640525f6106605260206105c060a46105dc5f855af16107e3573d5f5f3e3d5ffd5b60203d10615896576105c09050516105a0526108006105e0614d63565b6105e0516105c052600a546105e05261010036610600376105e05115610848576105c0516102805261083361070061441b565b61070060e0816107e05e5060e06107e06106205e5b6105805163f3207723610740526105a05161076052602435610780526040610740604461075c5f855af161087e573d5f5f3e3d5ffd5b60403d106158965761074090506040816107005e5061072051670de0b6b3a7640000810281670de0b6b3a76400008204186158965790506105c0518015615896578082049050905061074052610720516105805163638a1e8d610760526020610760600461077c845afa6108f4573d5f5f3e3d5ffd5b60203d10615896576107609050518060011c905010156109865760208061080052600d6107a0527f4465627420746f6f2068696768000000000000000000000000000000000000006107c0526107a08161080001602d82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06107e052806004016107fcfd5b6105e0511561099a5761064051151561099c565b5f5b610a395761074051610600525f6006555f600755610600516105e05180820182811061589657905090506005555f6004556009610560516020525f5260405f205415610a23575f610560517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef6009610560516020525f5260405f2054610760526020610760a35b5f6009610560516020525f5260405f2055610b09565b6106c0516105e052610620516004556106405161076052610740518060ff1c61589657610620518082038281135f83121861589657905090505f8112615896576107405261074051600555610680516007556106c051600a556105605115610ac5576106a0516009610560516020525f5260405f2055610560516040526106e051606052610ac5614dee565b6105e05161074051808202811583838304141715615896579050905061076051801561589657808204905090506105e0518082038281116158965790509050610600525b620f4240610600516105e05180820182811061589657905090501015610ba1576020806107c0526013610760527f52656d61696e64657220746f6f20736d616c6c0000000000000000000000000061078052610760816107c001603382825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06107a052806004016107bcfd5b604435610600511015610c26576020806107c0526008610760527f536c69707061676500000000000000000000000000000000000000000000000061078052610760816107c001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06107a052806004016107bcfd5b6105405160405261060051606052610c3c614ebc565b610c44614f29565b61054051337fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d76004356107605261060051610780526040610760a3662386f26fc1000061018052610c936151e8565b60206106005f5f5df35b637ba3152e8118610cbe573461589657662386f26fc100006102c052612765565b6390b229978118613ead57602436103417615896576004358060011c615896576040525f546060526060513b15610e3757336060518118610d00576001610db9565b60605163f851a440610140526020610140600461015c845afa610d25573d5f5f3e3d5ffd5b3d602081183d602010021880610140016101601161589657610140518060a01c6158965761018052506101809050518118610d61576001610db9565b60605163680c77836101a05260206101a060046101bc845afa610d86573d5f5f3e3d5ffd5b3d602081183d6020100218806101a0016101c011615896576101a0518060a01c615896576101e052506101e09050518118155b9050610eb257602080610280526006610220527f4163636573730000000000000000000000000000000000000000000000000000610240526102208161028001602682825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610260528060040161027cfd5b606051331815610eb25760208060e05260066080527f416363657373000000000000000000000000000000000000000000000000000060a05260808160e001602682825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a060c0528060040160dcfd5b6001546390b2299760805260405160a052803b15615896575f60806024609c5f855af1610ee1573d5f5f3e3d5ffd5b50005b63441a3e708118610f0357604436103417615896573361054052610f28565b630ad58d2f811861170f57606436103417615896576044358060a01c61589657610540525b5f5c6001146158965760015f5d600435610fb4576020806105c0526013610560527f5769746864726177696e67206e6f7468696e670000000000000000000000000061058052610560816105c001603382825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105a052806004016105bcfd5b6003546105605261056051338114610fd25761054051811415610fd4565b5f5b9050611052576020806106005260176105a0527f576974686472617720746f2f66726f6d207374616b65720000000000000000006105c0526105a08161060001603782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105e052806004016105fcfd5b600154639c868ac0610580526020610580600461059c845afa611077573d5f5f3e3d5ffd5b3d602081183d602010021880610580016105a01161589657610580518060011c615896576105c052506105c090505115611148576020806106605260226105e0527f576527726520646561642e2055736520656d657267656e63795f776974686472610600527f6177000000000000000000000000000000000000000000000000000000000000610620526105e08161066001604282825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610640528060040161065cfd5b6001546105805261115a610680614d63565b610680516107805261078051610280526111756106a061441b565b6106a060e0816105a05e5061064051610680526105a0516004556106005160075561068051600a5560043580620f424001620f424081106158965790506106805110156111ca576004356106805118156111cd565b60015b611249576020806107005260136106a0527f52656d61696e64657220746f6f20736d616c6c000000000000000000000000006106c0526106a08161070001603382825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06106e052806004016106fcfd5b610560511561127c57610620516009610560516020525f5260405f2055610560516040526106605160605261127c614dee565b6105a0515f8181185f8313021890505f8112615896576106a0526105805163ac6a2b5d610700526105c051670de0b6b3a7640000810281670de0b6b3a76400008204186158965790506105c0516106a05180820182811061589657905090508015615896578082049050905060043580820281158383830414171561589657905090506106805180156158965780820490509050610720526040610700602461071c5f855af161132e573d5f5f3e3d5ffd5b60403d106158965761070090506040816106c05e5060206158f05f395f516323b872dd61070052610580516107205230610740526106c051610760526020610700606461071c5f855af1611384573d5f5f3e3d5ffd5b3d61139b57803b15615896576001610780526113c5565b3d602081183d602010021880610700016107201161589657610700518060011c6158965761078052505b610780905051156158965760206158f05f395f5163512d6365610720526106c051610740525f610760526106e051610780525f6107a0526020610720608461073c5f855af1611416573d5f5f3e3d5ffd5b60203d1061589657610720905051610700523360405260043560605261143a6153a9565b6105c051610680516004358082038281116158965790509050808202811583838304141715615896579050905061068051801561589657808204905090506005557fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6105a05113611518576105a0516106805160043580820382811161589657905090508060ff1c6158965780820281191515600160ff1b84141517821584848405141716156158965790509050610680518060ff1c61589657801561589657808205600160ff1b8314155f19831415171561589657905090506004555b60243561070051101561159d57602080610780526008610720527f536c697070616765000000000000000000000000000000000000000000000000610740526107208161078001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610760528060040161077cfd5b60206159105f395f5163a9059cbb6107205261058051610740526106e051610760526020610720604461073c5f855af16115d9573d5f5f3e3d5ffd5b3d6115f057803b156158965760016107805261161a565b3d602081183d602010021880610720016107401161589657610720518060011c6158965761078052505b610780905051156158965760206159305f395f5163a9059cbb61072052610540516107405261070051610760526020610720604461073c5f855af1611661573d5f5f3e3d5ffd5b3d61167857803b15615896576001610780526116a2565b3d602081183d602010021880610720016107401161589657610720518060011c6158965761078052505b61078090505115615896576116b5614f29565b3361054051337ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db6107005161072052600435610740526040610720a4662386f26fc10000610180526117056151e8565b60206107005f5f5df35b639c868ac08118613ead5734615896576020600154639c868ac0604052602060406004605c845afa611743573d5f5f3e3d5ffd5b3d602081183d602010021880604001606011615896576040518060011c615896576080525060809050f35b63650dfae28118611b1757602436103417615896575f5c6001146158965761010036610540376001546106405261064051639c868ac0610660526020610660600461067c845afa6117c1573d5f5f3e3d5ffd5b3d602081183d602010021880610660016106801161589657610660518060011c615896576106a052506106a090505161183b576117ff610660613efd565b6106605161076052610760516102805261181a61068061441b565b61068060e0816107805e5060e06107806105605e6106005161054052611843565b600a54610540525b600435670de0b6b3a7640000810281670de0b6b3a7640000820418615896579050610540518015615896578082049050905061066052600161056051121561188b575f611892565b6105805115155b156118e25761066051610580518082028115838383041417156158965790509050610560515f81126158965761058051808201828110615896579050905080156158965780820490509050610660525b6106405163e0f038e76106a05260206106a060046106bc845afa611908573d5f5f3e3d5ffd5b60203d10615896576106a0905051610660518082028115838383041417156158965790509050670de0b6b3a7640000810490506106805261064051630b576c966106c05260206106c060046106dc845afa611965573d5f5f3e3d5ffd5b60203d10615896576106c0905051610660518082028115838383041417156158965790509050604052670de0b6b3a76400006060526119a5610700615416565b610700518060ff1c615896576106a05260206158f05f395f516318160ddd6106e05260206106e060046106fc845afa6119e0573d5f5f3e3d5ffd5b60203d10615896576106e09050516106c05260206158f05f395f51634903b0d1610720525f610740526020610720602461073c845afa611a22573d5f5f3e3d5ffd5b60203d10615896576107209050516106e05260206158f05f395f51634903b0d1610760526001610780526020610760602461077c845afa611a65573d5f5f3e3d5ffd5b60203d106158965761076090505161070052610680516106e05180820281158383830414171561589657905090506106c0518015615896578082049050905061072052610680516107005180820281158383830414171561589657905090506106c051801561589657808204905090506107405260406107206106e05e61070051610720526106e0518060ff1c615896576106a0518082038281135f8312186158965790509050610740526040610720f35b63ec46bf8f8118611b3b576024361034176158965733610540523361056052611c3b565b634189617d8118613ead57602436103417615896575f5c6001146158965760015f5d611b65615084565b600154631aa02d59610180526004356101a052803b15615896575f610180602461019c5f855af1611b98573d5f5f3e3d5ffd5b505f5f5d005b63b78be8028118611bcc57604436103417615896576024358060a01c61589657610540523361056052611c3b565b6370a082318118613ead57602436103417615896576004358060a01c6158965760405260096040516020525f5260405f205460605260206060f35b63d5569431811861274857606436103417615896576024358060a01c61589657610540526044358060a01c61589657610560525b5f5c6001146158965760015f5d6003546105805261058051610560518114611c695761054051811415611c6b565b5f5b9050611ce9576020806106205260176105c0527f576974686472617720746f2f66726f6d207374616b65720000000000000000006105e0526105c08161062001603782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610600528060040161061cfd5b610100366105a0376001546106a0526106a051639c868ac06106e05260206106e060046106fc845afa611d1e573d5f5f3e3d5ffd5b3d602081183d6020100218806106e00161070011615896576106e0518060011c6158965761072052506107209050516106c0526106c051611e6d5733610560511815611ddc5760208061074052600a6106e0527f4e6f74206b696c6c656400000000000000000000000000000000000000000000610700526106e08161074001602a82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610720528060040161073cfd5b611de76106e0614d63565b6106e0516107e0526107e05161028052611e0261070061441b565b61070060e0816108005e5060e06108006105c05e610660516105a0526105c0516004556105e051600555610620516007556105a051600a5561058051156120fc57610640516009610580516020525f5260405f205561058051604052610680516060526120fc614dee565b5f546106e0526106e0513b15611fe1576106e05163680c7783610700526020610700600461071c845afa611ea3573d5f5f3e3d5ffd5b3d602081183d602010021880610700016107201161589657610700518060a01c6158965761074052506107409050513318611f5e57610560516105405118156120f4576020806107c0526008610760527f726563656976657200000000000000000000000000000000000000000000000061078052610760816107c001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06107a052806004016107bcfd5b336105605118156120f457602080610760526005610700527f6f776e6572000000000000000000000000000000000000000000000000000000610720526107008161076001602582825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610740528060040161075cfd5b6106e051331861207157610560516105405118156120f457602080610760526008610700527f7265636569766572000000000000000000000000000000000000000000000000610720526107008161076001602882825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610740528060040161075cfd5b336105605118156120f457602080610760526005610700527f6f776e6572000000000000000000000000000000000000000000000000000000610720526107008161076001602582825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610740528060040161075cfd5b600a546105a0525b60043580620f424001620f424081106158965790506105a0511015612129576004356105a051181561212c565b60015b6121a8576020806107405260136106e0527f52656d61696e64657220746f6f20736d616c6c00000000000000000000000000610700526106e08161074001603382825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610720528060040161073cfd5b600435670de0b6b3a7640000810281670de0b6b3a76400008204186158965790506105a051801561589657808204905090506106e0526106e0518060ff1c615896576107005260016105c0511215612200575f612207565b6105e05115155b15612257576106e0516105e05180820281158383830414171561589657905090506105c0515f8112615896576105e0518082018281106158965790509050801561589657808204905090506106e0525b6106a05163ac6a2b5d610760526106e051610780526040610760602461077c5f855af1612286573d5f5f3e3d5ffd5b60403d106158965761076090506040816107205e5060206158f05f395f516323b872dd610760526106a05161078052306107a052610720516107c0526020610760606461077c5f855af16122dc573d5f5f3e3d5ffd5b3d6122f357803b156158965760016107e05261231d565b3d602081183d602010021880610760016107801161589657610760518060011c615896576107e052505b6107e0905051156158965760206158f05f395f51635b36389c6107a052610720516107c0526040366107e03760406107a060646107bc5f855af1612363573d5f5f3e3d5ffd5b60403d10615896576107a090506040816107605e50610760518060ff1c61589657610740518060ff1c615896578082038281135f83121861589657905090506107a052610560516040526004356060526123bb6153a9565b6005546105a051600435808203828111615896579050905080820281158383830414171561589657905090506105a051801561589657808204905090506005557fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600454131561242e576106c051612431565b60015b15612490576004546107005180670de0b6b3a764000003670de0b6b3a764000081135f83121861589657905080820281191515600160ff1b84141517821584848405141716156158965790509050670de0b6b3a7640000810590506004555b60016107a0511215612585577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6107a051136126155760206159105f395f516323b872dd6107c052336107e05230610800526107a0517f80000000000000000000000000000000000000000000000000000000000000008114615896575f035f8112615896576108205260206107c060646107dc5f855af1612534573d5f5f3e3d5ffd5b3d61254b57803b1561589657600161084052612575565b3d602081183d6020100218806107c0016107e011615896576107c0518060011c6158965761084052505b6108409050511561589657612615565b60206159105f395f5163a9059cbb6107c052610540516107e0526107a0515f8112615896576108005260206107c060446107dc5f855af16125c8573d5f5f3e3d5ffd5b3d6125df57803b1561589657600161082052612609565b3d602081183d6020100218806107c0016107e011615896576107c0518060011c6158965761082052505b61082090505115615896575b60206159105f395f5163a9059cbb6107c0526106a0516107e052610740516108005260206107c060446107dc5f855af1612651573d5f5f3e3d5ffd5b3d61266857803b1561589657600161082052612692565b3d602081183d6020100218806107c0016107e011615896576107c0518060011c6158965761082052505b610820905051156158965760206159305f395f5163a9059cbb6107c052610540516107e052610780516108005260206107c060446107dc5f855af16126d9573d5f5f3e3d5ffd5b3d6126f057803b156158965760016108205261271a565b3d602081183d6020100218806107c0016107e011615896576107c0518060011c6158965761082052505b61082090505115615896576106c05161273557612735614f29565b60406107806107c05e60406107c05f5f5df35b63e3e4bbbb8118613ead57602436103417615896576004356102c0525b5f5c6001146158965760015f5d6102c051610180526127826151e8565b5f5f5d005b638fa57492811861288a57346158965760035461054052600154639c868ac0610580526020610580600461059c845afa6127c3573d5f5f3e3d5ffd5b3d602081183d602010021880610580016105a01161589657610580518060011c615896576105c052506105c09050516105605261054051331861280a57610560511561280c565b5f5b156128885761281c610660614d63565b6106605161076052610760516102805261283761068061441b565b61068060e0816105805e50610580516004556105a0516005556105e05160075561062051600a55610600516009610540516020525f5260405f20556105405160405261064051606052612888614dee565b005b636d8934498118613ead573461589657600c5460405260206040f35b6399530b068118613ead5734615896576128c1610620613efd565b610620516107205261072051610280526128dc61064061441b565b61064060e0816105405e506105e05161290657670de0b6b3a7640000610620526020610620612943565b61056051670de0b6b3a7640000810281670de0b6b3a76400008204186158965790506105e051801561589657808204905090506106205260206106205bf35b636831c45b8118612bd457602436103417615896576004358060a01c61589657610180525f5c6001146158965760015f5d61297e615084565b600154156129fe5760208061020052600b6101a0527f416c7265616479207365740000000000000000000000000000000000000000006101c0526101a08161020001602b82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06101e052806004016101fcfd5b60206159105f395f51610180516393a397766101a05260206101a060046101bc845afa612a2d573d5f5f3e3d5ffd5b3d602081183d6020100218806101a0016101c011615896576101a0518060a01c615896576101e052506101e0905051186158965760206158f05f395f51610180516324bbab8b6101a05260206101a060046101bc845afa612a90573d5f5f3e3d5ffd5b3d602081183d6020100218806101a0016101c011615896576101a0518060a01c615896576101e052506101e09050511861589657306101805163a3cd68056101a05260206101a060046101bc845afa612aeb573d5f5f3e3d5ffd5b3d602081183d6020100218806101a0016101c011615896576101a0518060a01c615896576101e052506101e0905051186158965761018051600155610180516378f5e9f06101a05260206101a060046101bc845afa612b4c573d5f5f3e3d5ffd5b3d602081183d6020100218806101a0016101c011615896576101a0518060a01c615896576101e052506101e090505163858051e2610200526020610200600461021c845afa612b9d573d5f5f3e3d5ffd5b3d602081183d602010021880610200016102201161589657610200518060a01c6158965761024052506102409050516002555f5f5d005b63313ce5678118613ead573461589657601260405260206040f35b63e9333fab8118612e0957602436103417615896576004358060a01c61589657610180525f5c6001146158965760015f5d612c28615084565b610180513b15612dcf576101805163f851a4406101c05260206101c060046101dc845afa612c58573d5f5f3e3d5ffd5b3d602081183d6020100218806101c0016101e011615896576101c0518060a01c6158965761020052506102009050516101a0526101805163680c77836101c05260206101c060046101dc845afa612cb1573d5f5f3e3d5ffd5b3d602081183d6020100218806101c0016101e011615896576101c0518060a01c6158965761020052506102009050516101a0526101805163cab4d3db6101c05260206101c060046101dc845afa612d0a573d5f5f3e3d5ffd5b3d602081183d6020100218806101c0016101e011615896576101c0518060a01c6158965761020052506102009050516101a0526101805163d8b9a0186101c05260206101c060046101dc845afa612d63573d5f5f3e3d5ffd5b3d602081183d6020100218806101c0016101e011615896576101c0518060a01c6158965761020052506102009050516101a05261018051638c5710c96101e05260206101e060046101fc845afa612dbc573d5f5f3e3d5ffd5b60203d10615896576101e09050516101c0525b610180515f557f5a272403b402d892977df56625f4164ccaf70ca3863991c43ecfe76a6905b0a1610180516101a05260206101a0a15f5f5d005b6323b872dd8118613ead57606436103417615896576004358060a01c615896576108c0526024358060a01c615896576108e05260086108c0516020525f5260405f2080336020525f5260405f20905054610900527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6109005114612eb2576108c05160405233606052610900516044358082038281116158965790509050608052612eb2615841565b60406108c06105405e60443561058052612eca6154b8565b6001610920526020610920f35b63d4387a998118613ead57602436103417615896575f5c6001146158965760015f5d612f01615084565b60015463d4387a99610180526004356101a0526020610180602461019c5f855af1612f2e573d5f5f3e3d5ffd5b60203d106158965761018050505f5f5d005b6316126c508118612f7a5734615896577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61018052612fef565b63dd62ed3e8118613ead57604436103417615896576004358060a01c615896576040526024358060a01c6158965760605260086040516020525f5260405f20806060516020525f5260405f2090505460805260206080f35b63e5ef2aae8118613ead5760243610341761589657600435610180525b5f5c6001146158965760015f5d5f546101a052610180516101c052600c546101e052610180511961302657600b546101c052613036565b61302e615084565b61018051600b555b600154630469c15761020052803b15615896575f610200600461021c5f855af1613062573d5f5f3e3d5ffd5b506101e0516101c051116132fd576101e0516101c05110156133a6576001546378f5e9f0610220526020610220600461023c845afa6130a3573d5f5f3e3d5ffd5b3d602081183d602010021880610220016102401161589657610220518060a01c61589657610260525061026090505163ceb7f759610280526020610280600461029c5f855af16130f5573d5f5f3e3d5ffd5b60203d1061589657610280905051610200526102005160015463e0f038e7610220526020610220600461023c845afa613130573d5f5f3e3d5ffd5b60203d10615896576102209050518082028115838383041417156158965790509050670de0b6b3a7640000810490506101c05110156131e1576020806102c0526012610260527f4e6f7420656e6f75676820737461626c6573000000000000000000000000000061028052610260816102c001603282825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06102a052806004016102bcfd5b6101e0516101c051808203828111615896579050905060206159105f395f516370a0823161024052600154610260526020610240602461025c845afa613229573d5f5f3e3d5ffd5b60203d106158965761024090505180828118828410021890509050610220526101e0516102205180820382811161589657905090506101e05260206159105f395f516323b872dd61024052600154610260526101a05161028052610220516102a0526020610240606461025c5f855af16132a5573d5f5f3e3d5ffd5b3d6132bc57803b156158965760016102c0526132e6565b3d602081183d602010021880610240016102601161589657610240518060011c615896576102c052505b6102c090505115615896576101e051600c556133a6565b60206159105f395f516323b872dd610200526101a05161022052600154610240526101c0516101e0518082038281116158965790509050610260526020610200606461021c5f855af1613352573d5f5f3e3d5ffd5b3d61336957803b1561589657600161028052613393565b3d602081183d602010021880610200016102201161589657610200518060011c6158965761028052505b61028090505115615896576101c051600c555b6101a0517f6f16a992fba7b966942bb6064775e154f99de09fce962940d4e284c5c913286860406101c06102005e6040610200a25f5f5d005b6330c54085811861389c5734615896575f5c6001146158965760015f5d5f5461054052610540513b613483576020806105c052600c610560527f4e65656420666163746f7279000000000000000000000000000000000000000061058052610560816105c001602c82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105a052806004016105bcfd5b600154639c868ac0610560526020610560600461057c845afa6134a8573d5f5f3e3d5ffd5b3d602081183d602010021880610560016105801161589657610560518060011c615896576105a052506105a090505115613554576020806106205260066105c0527f4b696c6c656400000000000000000000000000000000000000000000000000006105e0526105c08161062001602682825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610600528060040161061cfd5b600154630469c15761056052803b15615896575f610560600461057c5f855af1613580573d5f5f3e3d5ffd5b506105405163cab4d3db610580526020610580600461059c845afa6135a7573d5f5f3e3d5ffd5b3d602081183d602010021880610580016105a01161589657610580518060a01c615896576105c052506105c0905051610560526105605161365a576020806105e052600f610580527f4e6f206665655f726563656976657200000000000000000000000000000000006105a052610580816105e001602f82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105c052806004016105dcfd5b600354610580526105805161056051186136e6576020806106005260136105a0527f5374616b65723d6665655f7265636569766572000000000000000000000000006105c0526105a08161060001603382825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06105e052806004016105fcfd5b6136f1610680614d63565b6106805161078052610780516102805261370c6106a061441b565b6106a060e0816105a05e505f6105a051121561379a576020806106e052601c610680527f4c6f7373206d6164652061646d696e20666565206e65676174697665000000006106a052610680816106e001603c82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a06106c052806004016106dcfd5b61064051600a556105c0516105a0515f811261589657808201828110615896579050905061068052610640516106805180820281158383830414171561589657905090506105c051801561589657808204905090506106405180820382811161589657905090506106a052610560516040526106a05160605261381b614ebc565b610680516005555f60045561060051600755610580511561386057610620516009610580516020525f5260405f20556105805160405261066051606052613860614dee565b7f78b4892921a904766a871a893b75e9461080a308419f358353f73739b29c7fc4610560516106c0526106a0516106e05260406106c0a15f5f5d005b6393d9d94c8118613ead57346158965760206158f060403960206040f35b63464d700b8118613acb57602436103417615896576004358060a01c615896576108c0525f5c6001146158965760015f5d6003541561396b576020806109405260126108e0527f5374616b657220616c7265616479207365740000000000000000000000000000610900526108e08161094001603282825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610920528060040161093cfd5b6108c051156158965761397c615084565b60096108c0516020525f5260405f20546108e0526108e05115613a95575f5463cab4d3db610920526020610920600461093c845afa6139bd573d5f5f3e3d5ffd5b3d602081183d602010021880610920016109401161589657610920518060a01c61589657610960525061096090505161090052610900516108c05118613a7557602080610980526013610920527f5374616b65723d6665655f726563656976657200000000000000000000000000610940526109208161098001603382825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610960528060040161097cfd5b6108c0516105405261090051610560526108e05161058052613a956154b8565b6108c0516003556108c0517f6901da73a3480b0bbd7deb64f259ff29a76ce811c7a3e5cf495f0e1eb4fb4dfb5f610900a25f5f5d005b63d70620058118613ae9573461589657602061593060403960206040f35b631a6865028118613ead57346158965760045460405260055460605260065460805260075460a05260806040f35b6395d89b418118613ead5734615896576020806101a0525f60036040527f79622d0000000000000000000000000000000000000000000000000000000000606052604080516020820183610160018151815250508083019250505060206159305f395f516395d89b41608052606060806004609c845afa613b9a573d5f5f3e3d5ffd5b3d606081183d60601002188060800160a0116158965760806080516080011061589657608051608001805182608001825160200183011161589657601d81116158965750603d816101005e5050610100905080516020820183610160018151815250508083019250505080610140526101409050816101a001604082825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506101a0f35b6306fdde038118613ead5734615896576020806101c0525f601a6040527f5969656c64204261736973206c697175696469747920666f7220000000000000606052604080516020820183610160018151815250508083019250505060206159305f395f516395d89b41608052606060806004609c845afa613cc7573d5f5f3e3d5ffd5b3d606081183d60601002188060800160a0116158965760806080516080011061589657608051608001805182608001825160200183011161589657601d81116158965750603d816101005e5050610100905080516020820183610160018151815250508083019250505080610140526101409050816101c001605782825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506101c0f35b63a9059cbb8118613dbe57604436103417615896576004358060a01c615896576108c05233610540526108c0516105605260243561058052613db16154b8565b60016108e05260206108e0f35b6318160ddd8118613ead573461589657600a5460405260206040f35b63095ea7b38118613ead57604436103417615896576004358060a01c6158965760c0523360405260c051606052602435608052613e15615841565b600160e052602060e0f35b6393a397768118613ead573461589657602061591060403960206040f35b63f851a4408118613ead5734615896575f5460405260206040f35b632a9439458118613ead57346158965760015460405260206040f35b63f5e34bfa8118613ead57346158965760025460405260206040f35b635ebaf1db8118613ead57346158965760035460405260206040f35b5f5ffd5b5f546040526040513b15613ef657604051638c5710c9606052602060606004607c845afa613ee1573d5f5f3e3d5ffd5b60203d10615896576060905051815250613efb565b5f8152505b565b60206158f05f395f516386fc88d3604052602060406004605c845afa613f25573d5f5f3e3d5ffd5b60203d1061589657604090505160025463a035b1fe608052602060806004609c845afa613f54573d5f5f3e3d5ffd5b60203d106158965760809050518082028115838383041417156158965790509050670de0b6b3a764000081049050815250565b6080516140035760208061012052601e60c0527f6d6174683a206d756c5f646976206469766973696f6e206279207a65726f000060e05260c08161012001603e82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610100528060040161011cfd5b6040516060517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8183099050905060c0526060516040510260e0525f6101005260e05160c0511061405e5760e05160c051036101005261406d565b600160e05160c0510303610100525b610100516140c45760a051614082575f61409e565b6040516060516080518015615896578082840990509050905015155b156140b557600160805160e05104018152506142b3565b60805160e051048152506142b3565b610100516080511161414857602080610180526016610120527f6d6174683a206d756c5f646976206f766572666c6f7700000000000000000000610140526101208161018001603682825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610160528060040161017cfd5b604051606051608051801561589657808284099050905090506101205260e05161012051111561417e5760016101005103610100525b6101205160e0510360e0526080516080515f0316610140526101405160805104610160526101405160e0510460e052600161014051610140515f0304016101405261014051610100510260e0511760e05260026101605160030218610180526101805161016051026002036101805102610180526101805161016051026002036101805102610180526101805161016051026002036101805102610180526101805161016051026002036101805102610180526101805161016051026002036101805102610180526101805161016051026002036101805102610180526101805160e051026101a05260a051614274575f614290565b6040516060516080518015615896578082840990509050905015155b156142ab576101a051600181018181106158965790506101a0525b6101a0518152505b565b610220516142c6575f815250614419565b6101e0517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8113156142f85780614305565b805f03811461589657805f035b90505f811261589657604052610200517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8113156143435780614350565b805f03811461589657805f035b90505f811261589657606052610220517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81131561438e578061439b565b805f03811461589657805f035b90505f8112615896576080525f60a0526143b6610260613f87565b610260518060ff1c61589657610240525f61022051125f61020051125f6101e0511214151461441157610240517f80000000000000000000000000000000000000000000000000000000000000008114615896575f03610240525b610240518152505b565b6004546102a0526005546102c0526006546102e05260075461030052600354610320525f6103405261032051156144685760096003546020525f5260405f20548060ff1c61589657610340525b600a548060ff1c61589657610360526144826103a0613eb1565b6103a05180670de0b6b3a764000003670de0b6b3a76400008111615896579050610340516ec097ce7bc90715b34b9f10000000008102816ec097ce7bc90715b34b9f100000000082051861589657905061036051801561589657808205600160ff1b8314155f1983141517156158965790509050806ec097ce7bc90715b34b9f1000000000036ec097ce7bc90715b34b9f100000000081135f8312186158965790505f8112615896578060b5710100000000000000000000000000000000008210614554578160801c91508060401b90505b69010000000000000000008210614572578160401c91508060201b90505b65010000000000821061458c578160201c91508060101b90505b630100000082106145a4578160101c91508060081b90505b620100008201810260121c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c9050808184040160011c905080830480828118828410021890509050905090508082028115838383041417156158965790509050670de0b6b3a76400008104905080670de0b6b3a764000003670de0b6b3a764000081116158965790508060ff1c6158965761038052600154635ef7feab6103c05260406103c060046103dc845afa614679573d5f5f3e3d5ffd5b60403d10615896576103c0905060208101905051670de0b6b3a7640000810281670de0b6b3a764000082041861589657905061028051801561589657808204905090508060ff1c615896576103a0526102c0518060ff1c615896576103c0526103a0516103c0516102a0518082018281125f83121861589657905090508082038281135f83121861589657905090506103e052610300518060ff1c61589657610400526102e0518060ff1c61589657610420525f6104405261042051610400518082038281135f83121861589657905090505f8181185f83130218905061046052662386f26fc100006103405112156147bf576103e0516103805180670de0b6b3a764000003670de0b6b3a764000081135f83121861589657905080820281191515600160ff1b84141517821584848405141716156158965790509050610440526148f2565b60016103e05112156147f6576103e051670de0b6b3a7640000810281670de0b6b3a7640000820518615896579050610440526148f2565b6103e051610460516103605180820281191515600160ff1b8414151782158484840514171615615896579050905061034051801561589657808205600160ff1b8314155f1983141517156158965790509050808281188284120218905090506104805261048051670de0b6b3a7640000810281670de0b6b3a76400008205186158965790506103e051610480518082038281135f83121861589657905090506103805180670de0b6b3a764000003670de0b6b3a764000081135f83121861589657905080820281191515600160ff1b841415178215848484051417161561589657905090508082018281125f8312186158965790509050610440525b6102a0516103e05161044051670de0b6b3a7640000810590508082038281135f83121861589657905090508082018281125f83121861589657905090506102a052610440516101e052610340516102005261036051610220526149566104a06142b5565b6104a05161048052600161044051126149a1576104805161046051670de0b6b3a7640000810281670de0b6b3a764000082051861589657905080828118828412021890509050610480525b6103c051670de0b6b3a7640000810281670de0b6b3a7640000820518615896579050610440518082018281125f83121861589657905090505f8181185f8313021890506104a05261040051670de0b6b3a7640000810281670de0b6b3a7640000820518615896579050610480518082018281125f83121861589657905090505f8181185f8313021890506104c0526104a0516104c0518082038281135f83121861589657905090506104e0526104a0516101e05261034051610200526104e05161022052614a706105006142b5565b610500516104c0516101e05261036051610200526104e05161022052614a976105206142b5565b610520518082038281135f83121861589657905090506104e0526103e0516103605180820281191515600160ff1b841415178215848484051417161561589657905090506103c0516103e0518082018281125f831218615896579050905060018101818112615896579050801561589657808205600160ff1b8314155f19831415171561589657905090506103805180670de0b6b3a764000003670de0b6b3a764000081135f83121861589657905080820281191515600160ff1b84141517821584848405141716156158965790509050655af3107a4000810590507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811315614ba15780614bae565b805f03811461589657805f035b90506105005260016103405112614be6576104e0516103405160018103818113615896579050808281188284120218905090506104e0525b60016103605112614c18576104e0516103605160018103818113615896579050808281188284120218905090506104e0525b5f6104e0511215614c6a576104e051610500517f80000000000000000000000000000000000000000000000000000000000000008114615896575f03808281188284130218905090506104e052614c84565b6104e05161050051808281188284120218905090506104e0525b69021e19e0c9bab23fffff6104a0516104c0518082038281135f831218615896579050905013614cc2576104e0515f8181185f8313021890506104e0525b6102a05181526104a051670de0b6b3a7640000810590505f81126158965760208201526102e05160408201526104c051670de0b6b3a7640000810590505f8112615896576060820152610340516104e0518082038281135f83121861589657905090505f8112615896576080820152610360516104e0518082038281135f83121861589657905090505f81126158965760a08201526104e05160c082015250565b60206158f05f395f516386fc88d3604052602060406004605c845afa614d8b573d5f5f3e3d5ffd5b60203d1061589657604090505160025463ceb7f759608052602060806004609c5f855af1614dbb573d5f5f3e3d5ffd5b60203d106158965760809050518082028115838383041417156158965790509050670de0b6b3a764000081049050815250565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60605113614e78576040515f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef6060517f80000000000000000000000000000000000000000000000000000000000000008114615896575f035f81126158965760805260206080a35b600160605112614eba575f6040517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef6060515f81126158965760805260206080a35b565b60096040516020525f5260405f2080546060518082018281106158965790509050815550600a546060518082018281106158965790509050600a556040515f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60605160805260206080a3565b60035460405260405115615082575f546060526060513b156150825760605163d8b9a01860a052602060a0600460bc845afa614f67573d5f5f3e3d5ffd5b3d602081183d60201002188060a00160c0116158965760a0518060a01c6158965760e0525060e090505160805260805115615082576080515a63a972985e60c452600460405160e45260200160c05260c0505f5f60c05160e05f8686f19050905060a05260a0516150825762030d405a10156150525760208061012052600360c0527f474153000000000000000000000000000000000000000000000000000000000060e05260c08161012001602382825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610100528060040161011cfd5b60805163a972985e60c452600460405160e45260200160c05260c0505f5f60c05160e05f8562030d40f1905060a0525b565b5f546040526040513b1561516b5760405133186150a25760016150f2565b60405163f851a440606052602060606004607c845afa6150c4573d5f5f3e3d5ffd5b3d602081183d602010021880606001608011615896576060518060a01c6158965760a0525060a09050513318155b6151e65760208061012052600660c0527f416363657373000000000000000000000000000000000000000000000000000060e05260c08161012001602682825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a0610100528060040161011cfd5b6040513318156151e65760208060c05260066060527f416363657373000000000000000000000000000000000000000000000000000060805260608160c001602682825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a060a0528060040160bcfd5b565b6001546101a052662386f26fc10001610180511061520857615208615084565b6101a0513314615247576101a051631e0cfcef6101c05260206101c060046101dc5f855af1615239573d5f5f3e3d5ffd5b60203d10615896576101c050505b60206159105f395f516370a082316101e052306102005260206101e060246101fc845afa615277573d5f5f3e3d5ffd5b60203d10615896576101e09050516101c0526101c051156153a7576101805180670de0b6b3a764000003670de0b6b3a764000081116158965790506101c051808202811583838304141715615896579050905060206158f05f395f516354f0f7d5610200526020610200600461021c845afa6152f5573d5f5f3e3d5ffd5b60203d1061589657610200905051801561589657808204905090506101e05260206158f05f395f516386514738610200526101c051610220525f610240526101e051610260525f6102805260016102a052602061020060a461021c5f855af1615360573d5f5f3e3d5ffd5b60203d10615896576102005050337f5bdcb9afddbd4bbebe8e11112e8b8ba0008f38b107b6c8d4bed13e9fb40acb1c60406101c06102005e61018051610240526060610200a25b565b60096040516020525f5260405f2080546060518082038281116158965790509050815550600a546060518082038281116158965790509050600a555f6040517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60605160805260206080a3565b60605161548e5760208060e052601f6080527f6d6174683a206365696c5f646976206469766973696f6e206279207a65726f0060a05260808160e001603f82825e8051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506308c379a060c0528060040160dcfd5b60405161549b575f6154b3565b60016060516040516001810381811161589657905004015b815250565b610560513081146154cb578015156154cd565b5f5b905015615896576003546105a0526105a05115615508576105a0516105405181186154f9576001615501565b610560518118155b905061550a565b5f5b6105c0526105c05115615799576105605161054051146158965760e0366105e0376105a051331861553c576001615591565b600154639c868ac06106c05260206106c060046106dc845afa615561573d5f5f3e3d5ffd5b3d602081183d6020100218806106c0016106e011615896576106c0518060011c6158965761070052506107009050515b61560e576155a06106c0614d63565b6106c0516107c0526107c051610280526155bb6106e061441b565b6106e060e0816107e05e5060e06107e06105e05e6105e0516004556106005160055561068051600a556106605160096105a0516020525f5260405f20556105a0516040526106a05160605261563f614dee565b600654610620526007546106405260055461060052600a546106805260096105a0516020525f5260405f2054610660525b6105a05161054051186156bf57610640516106805161060051610580518082028115838383041417156158965790509050048082038281116158965790509050610640526106605161062051610660516105805180820382811161589657905090508082028115838383041417156158965790509050046106205261578a565b6105a051610560511861578a576106005161058051808202811583838304141715615896579050905061068051801561589657808204905090506106c052610640516106c0518082018281106158965790509050610640526402540be40161066051101561574657610620516106c05180820182811061589657905090506106205261578a565b610620516106605161058051808201828110615896579050905080820281158383830414171561589657905090506106605180156158965780820490509050610620525b61064051600755610620516006555b6009610540516020525f5260405f2080546105805180820382811161589657905090508155506009610560516020525f5260405f2080546105805180820182811061589657905090508155506105c0516157f3575f6157fb565b6105a0513314155b1561580857615808614f29565b61056051610540517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef610580516105e05260206105e0a3565b60805160086040516020525f5260405f20806060516020525f5260405f209050556060516040517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560805160a052602060a0a3565b5f80fd3ead3ead176e3ead3ead3d711b9e3c4438ba29453ead3e3e00383ead3ead03e205bd3ead2f402bef3dda0ee433df3e203ead3ead3ead1c072ed73b173e912fd227873ead3ead3ead3ead3e7528a63e593ead3ead0018855820b92d4481959e313f8014e1224d3517d97d1b4c3df4f589480d4933fde4268a751958f08118561860a16576797065728300040300380000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c599000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e000000000000000000000000d9ff8396554a0d18b2cfbec53e1979b7ecce8373000000000000000000000000370a449febb9411c95bf897021377fe0b7d100c0","name":"LT","is_blueprint":false,"license_type":"none","is_fully_verified":false,"is_verified_via_eth_bytecode_db":true,"language":"vyper","evm_version":null,"can_be_visualized_via_sol2uml":false,"is_verified_via_sourcify":false,"additional_sources":[{"file_path":".venv/lib/pypy3.11/site-packages/snekmate/utils/math.vy","source_code":"# pragma version ~=0.4.3\n# pragma nonreentrancy off\n\"\"\"\n@title Standard Mathematical Utility Functions\n@custom:contract-name math\n@license GNU Affero General Public License v3.0 only\n@author pcaversaccio\n@custom:coauthor bout3fiddy\n@notice These functions implement standard mathematical utility\n        functions that are missing in the Vyper language. If a\n        function is inspired by an existing implementation, it\n        is properly referenced in the function docstring. The\n        following functions have been added for convenience:\n        - `_uint256_average` (`internal` `pure` function),\n        - `_int256_average` (`internal` `pure` function),\n        - `_ceil_div` (`internal` `pure` function),\n        - `_signum` (`internal` `pure` function),\n        - `_mul_div` (`internal` `pure` function),\n        - `_log2` (`internal` `pure` function),\n        - `_log10` (`internal` `pure` function),\n        - `_log256` (`internal` `pure` function),\n        - `_wad_ln` (`internal` `pure` function),\n        - `_wad_exp` (`internal` `pure` function),\n        - `_cbrt` (`internal` `pure` function),\n        - `_wad_cbrt` (`internal` `pure` function).\n\"\"\"\n\n\n@deploy\n@payable\ndef __init__():\n    \"\"\"\n    @dev To omit the opcodes for checking the `msg.value`\n         in the creation-time EVM bytecode, the constructor\n         is declared as `payable`.\n    \"\"\"\n    pass\n\n\n@internal\n@pure\ndef _uint256_average(x: uint256, y: uint256) -> uint256:\n    \"\"\"\n    @dev Returns the average of two 32-byte unsigned integers.\n    @notice Note that the result is rounded towards zero. For\n            more details on finding the average of two unsigned\n            integers without an overflow, please refer to:\n            https://devblogs.microsoft.com/oldnewthing/20220207-00/?p=106223.\n    @param x The first 32-byte unsigned integer of the data set.\n    @param y The second 32-byte unsigned integer of the data set.\n    @return uint256 The 32-byte average (rounded towards zero) of\n            `x` and `y`.\n    \"\"\"\n    return unsafe_add(x & y, (x ^ y) >> 1)\n\n\n@internal\n@pure\ndef _int256_average(x: int256, y: int256) -> int256:\n    \"\"\"\n    @dev Returns the average of two 32-byte signed integers.\n    @notice Note that the result is rounded towards infinity.\n            For more details on finding the average of two signed\n            integers without an overflow, please refer to:\n            https://patents.google.com/patent/US6007232A/en.\n    @param x The first 32-byte signed integer of the data set.\n    @param y The second 32-byte signed integer of the data set.\n    @return int256 The 32-byte average (rounded towards infinity)\n            of `x` and `y`.\n    \"\"\"\n    return unsafe_add(unsafe_add(x >> 1, y >> 1), x & y & 1)\n\n\n@internal\n@pure\ndef _ceil_div(x: uint256, y: uint256) -> uint256:\n    \"\"\"\n    @dev Calculates \"ceil(x / y)\" for any strictly positive `y`.\n    @notice The implementation is inspired by OpenZeppelin's\n            implementation here:\n            https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/Math.sol.\n    @param x The 32-byte numerator.\n    @param y The 32-byte denominator.\n    @return uint256 The 32-byte rounded up result of \"x/y\".\n    \"\"\"\n    assert y != empty(uint256), \"math: ceil_div division by zero\"\n    # Due to a known compiler bug (https://github.com/vyperlang/vyper/issues/3480),\n    # we use `0` instead of `empty(uint256)` as return value.\n    return 0 if (x == empty(uint256)) else unsafe_add(unsafe_div(x - 1, y), 1)\n\n\n@internal\n@pure\ndef _signum(x: int256) -> int256:\n    \"\"\"\n    @dev Returns the indication of the sign of a 32-byte signed integer.\n    @notice The function returns `-1` if `x < 0`, `0` if `x == 0`, and `1`\n            if `x > 0`. For more details on finding the sign of a signed\n            integer, please refer to:\n            https://graphics.stanford.edu/~seander/bithacks.html#CopyIntegerSign.\n    @param x The 32-byte signed integer variable.\n    @return int256 The 32-byte sign indication (`1`, `0`, or `-1`) of `x`.\n    \"\"\"\n    return unsafe_sub(convert((x > empty(int256)), int256), convert((x < empty(int256)), int256))\n\n\n@internal\n@pure\ndef _mul_div(x: uint256, y: uint256, denominator: uint256, roundup: bool) -> uint256:\n    \"\"\"\n    @dev Calculates \"(x * y) / denominator\" in 512-bit precision,\n         following the selected rounding direction.\n    @notice The implementation is inspired by Remco Bloemen's\n            implementation under the MIT license here:\n            https://xn--2-umb.com/21/muldiv.\n            Furthermore, the rounding direction design pattern is\n            inspired by OpenZeppelin's implementation here:\n            https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/Math.sol.\n    @param x The 32-byte multiplicand.\n    @param y The 32-byte multiplier.\n    @param denominator The 32-byte divisor.\n    @param roundup The Boolean variable that specifies whether\n           to round up or not. The default `False` is round down.\n    @return uint256 The 32-byte calculation result.\n    \"\"\"\n    # Handle division by zero.\n    assert denominator != empty(uint256), \"math: mul_div division by zero\"\n\n    # 512-bit multiplication \"[prod1 prod0] = x * y\".\n    # Compute the product \"mod 2**256\" and \"mod 2**256 - 1\".\n    # Then use the Chinese Remainder theorem to reconstruct\n    # the 512-bit result. The result is stored in two 256-bit\n    # variables, where: \"product = prod1 * 2**256 + prod0\".\n    mm: uint256 = uint256_mulmod(x, y, max_value(uint256))\n    # The least significant 256 bits of the product.\n    prod0: uint256 = unsafe_mul(x, y)\n    # The most significant 256 bits of the product.\n    prod1: uint256 = empty(uint256)\n\n    if mm < prod0:\n        prod1 = unsafe_sub(unsafe_sub(mm, prod0), 1)\n    else:\n        prod1 = unsafe_sub(mm, prod0)\n\n    # Handling of non-overflow cases, 256 by 256 division.\n    if prod1 == empty(uint256):\n        if roundup and uint256_mulmod(x, y, denominator) != empty(uint256):\n            # Calculate \"ceil((x * y) / denominator)\". The following\n            # line cannot overflow because we have the previous check\n            # \"(x * y) % denominator != 0\", which accordingly rules out\n            # the possibility of \"x * y = 2**256 - 1\" and `denominator == 1`.\n            return unsafe_add(unsafe_div(prod0, denominator), 1)\n\n        return unsafe_div(prod0, denominator)\n\n    # Ensure that the result is less than \"2**256\". Also,\n    # prevents that `denominator == 0`.\n    assert denominator > prod1, \"math: mul_div overflow\"\n\n    #######################\n    # 512 by 256 Division #\n    #######################\n\n    # Make division exact by subtracting the remainder\n    # from \"[prod1 prod0]\". First, compute remainder using\n    # the `uint256_mulmod` operation.\n    remainder: uint256 = uint256_mulmod(x, y, denominator)\n\n    # Second, subtract the 256-bit number from the 512-bit\n    # number.\n    if remainder > prod0:\n        prod1 = unsafe_sub(prod1, 1)\n    prod0 = unsafe_sub(prod0, remainder)\n\n    # Factor powers of two out of the denominator and calculate\n    # the largest power of two divisor of denominator. Always `>= 1`,\n    # unless the denominator is zero (which is prevented above),\n    # in which case `twos` is zero. For more details, please refer to:\n    # https://cs.stackexchange.com/q/138556.\n    twos: uint256 = unsafe_sub(empty(uint256), denominator) & denominator\n    # Divide denominator by `twos`.\n    denominator_div: uint256 = unsafe_div(denominator, twos)\n    # Divide \"[prod1 prod0]\" by `twos`.\n    prod0 = unsafe_div(prod0, twos)\n    # Flip `twos` such that it is \"2**256 / twos\". If `twos` is zero,\n    # it becomes one.\n    twos = unsafe_add(unsafe_div(unsafe_sub(empty(uint256), twos), twos), 1)\n\n    # Shift bits from `prod1` to `prod0`.\n    prod0 |= unsafe_mul(prod1, twos)\n\n    # Invert the denominator \"mod 2**256\". Since the denominator is\n    # now an odd number, it has an inverse modulo \"2**256\", so we have:\n    # \"denominator * inverse = 1 mod 2**256\". Calculate the inverse by\n    # starting with a seed that is correct for four bits. That is,\n    # \"denominator * inverse = 1 mod 2**4\".\n    inverse: uint256 = unsafe_mul(3, denominator_div) ^ 2\n\n    # Use Newton-Raphson iteration to improve accuracy. Thanks to Hensel's\n    # lifting lemma, this also works in modular arithmetic by doubling the\n    # correct bits in each step.\n    inverse = unsafe_mul(inverse, unsafe_sub(2, unsafe_mul(denominator_div, inverse))) # Inverse \"mod 2**8\".\n    inverse = unsafe_mul(inverse, unsafe_sub(2, unsafe_mul(denominator_div, inverse))) # Inverse \"mod 2**16\".\n    inverse = unsafe_mul(inverse, unsafe_sub(2, unsafe_mul(denominator_div, inverse))) # Inverse \"mod 2**32\".\n    inverse = unsafe_mul(inverse, unsafe_sub(2, unsafe_mul(denominator_div, inverse))) # Inverse \"mod 2**64\".\n    inverse = unsafe_mul(inverse, unsafe_sub(2, unsafe_mul(denominator_div, inverse))) # Inverse \"mod 2**128\".\n    inverse = unsafe_mul(inverse, unsafe_sub(2, unsafe_mul(denominator_div, inverse))) # Inverse \"mod 2**256\".\n\n    # Since the division is now exact, we can divide by multiplying\n    # with the modular inverse of the denominator. This returns the\n    # correct result modulo \"2**256\". Since the preconditions guarantee\n    # that the result is less than \"2**256\", this is the final result.\n    # We do not need to calculate the high bits of the result and\n    # `prod1` is no longer necessary.\n    result: uint256 = unsafe_mul(prod0, inverse)\n\n    if roundup and uint256_mulmod(x, y, denominator) != empty(uint256):\n        # Calculate \"ceil((x * y) / denominator)\". The following\n        # line uses intentionally checked arithmetic to prevent\n        # a theoretically possible overflow.\n        result += 1\n\n    return result\n\n\n@internal\n@pure\ndef _log2(x: uint256, roundup: bool) -> uint256:\n    \"\"\"\n    @dev Returns the log in base 2 of `x`, following the selected\n         rounding direction.\n    @notice Note that it returns `0` if given `0`. The implementation\n            is inspired by OpenZeppelin's implementation here:\n            https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/Math.sol.\n    @param x The 32-byte variable.\n    @param roundup The Boolean variable that specifies whether\n           to round up or not. The default `False` is round down.\n    @return uint256 The 32-byte calculation result.\n    \"\"\"\n    # For the special case `x == 0`, we already return `0` here in order\n    # not to iterate through the remaining code.\n    if x == empty(uint256):\n        return empty(uint256)\n\n    value: uint256 = x\n    result: uint256 = empty(uint256)\n\n    # The following lines cannot overflow because we have the well-known\n    # decay behaviour of `log2(max_value(uint256)) < max_value(uint256)`.\n    if x >> 128 != empty(uint256):\n        x >>= 128\n        result = 128\n    if x >> 64 != empty(uint256):\n        x >>= 64\n        result = unsafe_add(result, 64)\n    if x >> 32 != empty(uint256):\n        x >>= 32\n        result = unsafe_add(result, 32)\n    if x >> 16 != empty(uint256):\n        x >>= 16\n        result = unsafe_add(result, 16)\n    if x >> 8 != empty(uint256):\n        x >>= 8\n        result = unsafe_add(result, 8)\n    if x >> 4 != empty(uint256):\n        x >>= 4\n        result = unsafe_add(result, 4)\n    if x >> 2 != empty(uint256):\n        x >>= 2\n        result = unsafe_add(result, 2)\n    if x >> 1 != empty(uint256):\n        result = unsafe_add(result, 1)\n\n    if roundup and (1 << result) < value:\n        result = unsafe_add(result, 1)\n\n    return result\n\n\n@internal\n@pure\ndef _log10(x: uint256, roundup: bool) -> uint256:\n    \"\"\"\n    @dev Returns the log in base 10 of `x`, following the selected\n         rounding direction.\n    @notice Note that it returns `0` if given `0`. The implementation\n            is inspired by OpenZeppelin's implementation here:\n            https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/Math.sol.\n    @param x The 32-byte variable.\n    @param roundup The Boolean variable that specifies whether\n           to round up or not. The default `False` is round down.\n    @return uint256 The 32-byte calculation result.\n    \"\"\"\n    # For the special case `x == 0`, we already return `0` here in order\n    # not to iterate through the remaining code.\n    if x == empty(uint256):\n        return empty(uint256)\n\n    value: uint256 = x\n    result: uint256 = empty(uint256)\n\n    # The following lines cannot overflow because we have the well-known\n    # decay behaviour of `log10(max_value(uint256)) < max_value(uint256)`.\n    if x >= 10 ** 64:\n        x = unsafe_div(x, 10 ** 64)\n        result = 64\n    if x >= 10 ** 32:\n        x = unsafe_div(x, 10 ** 32)\n        result = unsafe_add(result, 32)\n    if x >= 10 ** 16:\n        x = unsafe_div(x, 10 ** 16)\n        result = unsafe_add(result, 16)\n    if x >= 10 ** 8:\n        x = unsafe_div(x, 10 ** 8)\n        result = unsafe_add(result, 8)\n    if x >= 10 ** 4:\n        x = unsafe_div(x, 10 ** 4)\n        result = unsafe_add(result, 4)\n    if x >= 10 ** 2:\n        x = unsafe_div(x, 10 ** 2)\n        result = unsafe_add(result, 2)\n    if x >= 10:\n        result = unsafe_add(result, 1)\n\n    if roundup and (10 ** result) < value:\n        result = unsafe_add(result, 1)\n\n    return result\n\n\n@internal\n@pure\ndef _log256(x: uint256, roundup: bool) -> uint256:\n    \"\"\"\n    @dev Returns the log in base 256 of `x`, following the selected\n         rounding direction.\n    @notice Note that it returns `0` if given `0`. Also, adding one to the\n            rounded down result gives the number of pairs of hex symbols\n            needed to represent `x` as a hex string. The implementation is\n            inspired by OpenZeppelin's implementation here:\n            https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/Math.sol.\n    @param x The 32-byte variable.\n    @param roundup The Boolean variable that specifies whether\n           to round up or not. The default `False` is round down.\n    @return uint256 The 32-byte calculation result.\n    \"\"\"\n    # For the special case `x == 0`, we already return `0` here in order\n    # not to iterate through the remaining code.\n    if x == empty(uint256):\n        return empty(uint256)\n\n    value: uint256 = x\n    result: uint256 = empty(uint256)\n\n    # The following lines cannot overflow because we have the well-known\n    # decay behaviour of `log256(max_value(uint256)) < max_value(uint256)`.\n    if x >> 128 != empty(uint256):\n        x >>= 128\n        result = 16\n    if x >> 64 != empty(uint256):\n        x >>= 64\n        result = unsafe_add(result, 8)\n    if x >> 32 != empty(uint256):\n        x >>= 32\n        result = unsafe_add(result, 4)\n    if x >> 16 != empty(uint256):\n        x >>= 16\n        result = unsafe_add(result, 2)\n    if x >> 8 != empty(uint256):\n        result = unsafe_add(result, 1)\n\n    if roundup and (1 << (result << 3)) < value:\n        result = unsafe_add(result, 1)\n\n    return result\n\n\n@internal\n@pure\ndef _wad_ln(x: int256) -> int256:\n    \"\"\"\n    @dev Calculates the natural logarithm of a signed integer with a\n         precision of 1e18.\n    @notice Note that it returns `0` if given `0`. Furthermore, this function\n            consumes about 1,400 to 1,650 gas units depending on the value\n            of `x`. The implementation is inspired by Remco Bloemen's\n            implementation under the MIT license here:\n            https://xn--2-umb.com/22/exp-ln.\n    @param x The 32-byte variable.\n    @return int256 The 32-byte calculation result.\n    \"\"\"\n    assert x >= empty(int256), \"math: wad_ln undefined\"\n\n    # For the special case `x == 0`, we already return `0` here in order\n    # not to iterate through the remaining code.\n    if x == empty(int256):\n        return empty(int256)\n\n    # We want to convert `x` from \"10**18\" fixed point to \"2**96\"\n    # fixed point. We do this by multiplying by \"2**96 / 10**18\".\n    # But since \"ln(x * C) = ln(x) + ln(C)\" holds, we can just do\n    # nothing here and add \"ln(2**96 / 10**18)\" at the end.\n\n    # Reduce the range of `x` to \"(1, 2) * 2**96\".\n    # Also remember that \"ln(2**k * x) = k * ln(2) + ln(x)\" holds.\n    k: int256 = unsafe_sub(convert(self._log2(convert(x, uint256), False), int256), 96)\n    # Note that to circumvent Vyper's safecast feature for the potentially\n    # negative expression `x <<= uint256(159 - k)`, we first convert the\n    # expression `x <<= uint256(159 - k)` to `bytes32` and subsequently\n    # to `uint256`. Remember that the EVM default behaviour is to use two's\n    # complement representation to handle signed integers.\n    x = convert(convert(convert(x << convert(unsafe_sub(159, k), uint256), bytes32), uint256) >> 159, int256)\n\n    # Evaluate using a \"(8, 8)\"-term rational approximation. Since `p` is monic,\n    # we will multiply by a scaling factor later.\n    p: int256 = unsafe_add(\n        unsafe_mul(unsafe_add(x, 3_273_285_459_638_523_848_632_254_066_296), x) >> 96,\n        24_828_157_081_833_163_892_658_089_445_524,\n    )\n    p = unsafe_add(unsafe_mul(p, x) >> 96, 43_456_485_725_739_037_958_740_375_743_393)\n    p = unsafe_sub(unsafe_mul(p, x) >> 96, 11_111_509_109_440_967_052_023_855_526_967)\n    p = unsafe_sub(unsafe_mul(p, x) >> 96, 45_023_709_667_254_063_763_336_534_515_857)\n    p = unsafe_sub(unsafe_mul(p, x) >> 96, 14_706_773_417_378_608_786_704_636_184_526)\n    p = unsafe_sub(unsafe_mul(p, x), 795_164_235_651_350_426_258_249_787_498 << 96)\n\n    # We leave `p` in the \"2**192\" base so that we do not have to scale it up\n    # again for the division. Note that `q` is monic by convention.\n    q: int256 = unsafe_add(\n        unsafe_mul(unsafe_add(x, 5_573_035_233_440_673_466_300_451_813_936), x) >> 96,\n        71_694_874_799_317_883_764_090_561_454_958,\n    )\n    q = unsafe_add(unsafe_mul(q, x) >> 96, 283_447_036_172_924_575_727_196_451_306_956)\n    q = unsafe_add(unsafe_mul(q, x) >> 96, 401_686_690_394_027_663_651_624_208_769_553)\n    q = unsafe_add(unsafe_mul(q, x) >> 96, 204_048_457_590_392_012_362_485_061_816_622)\n    q = unsafe_add(unsafe_mul(q, x) >> 96, 31_853_899_698_501_571_402_653_359_427_138)\n    q = unsafe_add(unsafe_mul(q, x) >> 96, 909_429_971_244_387_300_277_376_558_375)\n\n    # It is known that the polynomial `q` has no zeros in the domain.\n    # No scaling is required, as `p` is already \"2**96\" too large. Also,\n    # `r` is in the range \"(0, 0.125) * 2**96\" after the division.\n    r: int256 = unsafe_div(p, q)\n\n    # To finalise the calculation, we have to proceed with the following steps:\n    #   - multiply by the scaling factor \"s = 5.549...\",\n    #   - add \"ln(2**96 / 10**18)\",\n    #   - add \"k * ln(2)\", and\n    #   - multiply by \"10**18 / 2**96 = 5**18 >> 78\".\n    # In order to perform the most gas-efficient calculation, we carry out all\n    # these steps in one expression.\n    return (\n        unsafe_add(\n            unsafe_add(\n                unsafe_mul(r, 1_677_202_110_996_718_588_342_820_967_067_443_963_516_166),\n                unsafe_mul(\n                    k, 16_597_577_552_685_614_221_487_285_958_193_947_469_193_820_559_219_878_177_908_093_499_208_371\n                ),\n            ),\n            600_920_179_829_731_861_736_702_779_321_621_459_595_472_258_049_074_101_567_377_883_020_018_308,\n        )\n        >> 174\n    )\n\n\n@internal\n@pure\ndef _wad_exp(x: int256) -> int256:\n    \"\"\"\n    @dev Calculates the natural exponential function of a signed integer with\n         a precision of 1e18.\n    @notice Note that this function consumes about 810 gas units. The implementation\n            is inspired by Remco Bloemen's implementation under the MIT license here:\n            https://xn--2-umb.com/22/exp-ln.\n    @param x The 32-byte variable.\n    @return int256 The 32-byte calculation result.\n    \"\"\"\n    # If the result is `< 1`, we return zero. This happens when we have the following:\n    # \"x <= (log(1e-18) * 1e18) ~ -4.15e19\".\n    if x <= -41_446_531_673_892_822_313:\n        return empty(int256)\n\n    # When the result is \"> (2**255 - 1) / 1e18\" we cannot represent it as a signed integer.\n    # This happens when \"x >= floor(log((2**255 - 1) / 1e18) * 1e18) ~ 135\".\n    assert x < 135_305_999_368_893_231_589, \"math: wad_exp overflow\"\n\n    # `x` is now in the range \"(-42, 136) * 1e18\". Convert to \"(-42, 136) * 2**96\" for higher\n    # intermediate precision and a binary base. This base conversion is a multiplication with\n    # \"1e18 / 2**96 = 5**18 / 2**78\".\n    x = unsafe_div(x << 78, 5 ** 18)\n\n    # Reduce the range of `x` to \"(-½ ln 2, ½ ln 2) * 2**96\" by factoring out powers of two\n    # so that \"exp(x) = exp(x') * 2**k\", where `k` is a signer integer. Solving this gives\n    # \"k = round(x / log(2))\" and \"x' = x - k * log(2)\". Thus, `k` is in the range \"[-61, 195]\".\n    k: int256 = unsafe_add(unsafe_div(x << 96, 54_916_777_467_707_473_351_141_471_128), 2 ** 95) >> 96\n    x = unsafe_sub(x, unsafe_mul(k, 54_916_777_467_707_473_351_141_471_128))\n\n    # Evaluate using a \"(6, 7)\"-term rational approximation. Since `p` is monic,\n    # we will multiply by a scaling factor later.\n    y: int256 = unsafe_add(\n        unsafe_mul(unsafe_add(x, 1_346_386_616_545_796_478_920_950_773_328), x) >> 96,\n        57_155_421_227_552_351_082_224_309_758_442,\n    )\n    p: int256 = unsafe_add(\n        unsafe_mul(\n            unsafe_add(\n                unsafe_mul(unsafe_sub(unsafe_add(y, x), 94_201_549_194_550_492_254_356_042_504_812), y) >> 96,\n                28_719_021_644_029_726_153_956_944_680_412_240,\n            ),\n            x,\n        ),\n        4_385_272_521_454_847_904_659_076_985_693_276 << 96,\n    )\n\n    # We leave `p` in the \"2**192\" base so that we do not have to scale it up\n    # again for the division.\n    q: int256 = unsafe_add(\n        unsafe_mul(unsafe_sub(x, 2_855_989_394_907_223_263_936_484_059_900), x) >> 96,\n        50_020_603_652_535_783_019_961_831_881_945,\n    )\n    q = unsafe_sub(unsafe_mul(q, x) >> 96, 533_845_033_583_426_703_283_633_433_725_380)\n    q = unsafe_add(unsafe_mul(q, x) >> 96, 3_604_857_256_930_695_427_073_651_918_091_429)\n    q = unsafe_sub(unsafe_mul(q, x) >> 96, 14_423_608_567_350_463_180_887_372_962_807_573)\n    q = unsafe_add(unsafe_mul(q, x) >> 96, 26_449_188_498_355_588_339_934_803_723_976_023)\n\n    # The polynomial `q` has no zeros in the range because all its roots are complex.\n    # No scaling is required, as `p` is already \"2**96\" too large. Also,\n    # `r` is in the range \"(0.09, 0.25) * 2**96\" after the division.\n    r: int256 = unsafe_div(p, q)\n\n    # To finalise the calculation, we have to multiply `r` by:\n    #   - the scale factor \"s = ~6.031367120\",\n    #   - the factor \"2**k\" from the range reduction, and\n    #   - the factor \"1e18 / 2**96\" for the base conversion.\n    # We do this all at once, with an intermediate result in \"2**213\" base,\n    # so that the final right shift always gives a positive value.\n\n    # Note that to circumvent Vyper's safecast feature for the potentially\n    # negative parameter value `r`, we first convert `r` to `bytes32` and\n    # subsequently to `uint256`. Remember that the EVM default behaviour is\n    # to use two's complement representation to handle signed integers.\n    return convert(\n        unsafe_mul(\n            convert(convert(r, bytes32), uint256), 3_822_833_074_963_236_453_042_738_258_902_158_003_155_416_615_667\n        )\n        >> convert(unsafe_sub(195, k), uint256),\n        int256,\n    )\n\n\n@internal\n@pure\ndef _cbrt(x: uint256, roundup: bool) -> uint256:\n    \"\"\"\n    @dev Calculates the cube root of an unsigned integer.\n    @notice Note that this function consumes about 1,600 to 1,800 gas units\n            depending on the value of `x` and `roundup`. The implementation is\n            inspired by Curve Finance's implementation under the MIT license here:\n            https://github.com/curvefi/tricrypto-ng/blob/main/contracts/main/CurveCryptoMathOptimized3.vy.\n    @param x The 32-byte variable from which the cube root is calculated.\n    @param roundup The Boolean variable that specifies whether\n           to round up or not. The default `False` is round down.\n    @return The 32-byte cube root of `x`.\n    \"\"\"\n    # For the special case `x == 0`, we already return `0` here in order\n    # not to iterate through the remaining code.\n    if x == empty(uint256):\n        return empty(uint256)\n\n    y: uint256 = unsafe_div(self._wad_cbrt(x), 10 ** 12)\n\n    if roundup and unsafe_mul(unsafe_mul(y, y), y) != x:\n        y = unsafe_add(y, 1)\n\n    return y\n\n\n@internal\n@pure\ndef _wad_cbrt(x: uint256) -> uint256:\n    \"\"\"\n    @dev Calculates the cube root of an unsigned integer with a precision\n         of 1e18.\n    @notice Note that this function consumes about 1,500 to 1,700 gas units\n            depending on the value of `x`. The implementation is inspired\n            by Curve Finance's implementation under the MIT license here:\n            https://github.com/curvefi/tricrypto-ng/blob/main/contracts/main/CurveCryptoMathOptimized3.vy.\n    @param x The 32-byte variable from which the cube root is calculated.\n    @return The 32-byte cubic root of `x` with a precision of 1e18.\n    \"\"\"\n    # For the special case `x == 0`, we already return `0` here in order\n    # not to iterate through the remaining code.\n    if x == empty(uint256):\n        return empty(uint256)\n\n    # Since this cube root is for numbers with base 1e18, we have to scale\n    # the input by 1e36 to increase the precision. This leads to an overflow\n    # for very large numbers. So we conditionally sacrifice precision.\n    value: uint256 = empty(uint256)\n    if x >= unsafe_mul(unsafe_div(max_value(uint256), 10 ** 36), 10 ** 18):\n        value = x\n    elif x >= unsafe_div(max_value(uint256), 10 ** 36):\n        value = unsafe_mul(x, 10 ** 18)\n    else:\n        value = unsafe_mul(x, 10 ** 36)\n\n    # Compute the binary logarithm of `value`.\n    log2x: uint256 = self._log2(value, False)\n\n    # If we divide log2x by 3, the remainder is \"log2x % 3\". So if we simply\n    # multiply \"2**(log2x/3)\" and discard the remainder to calculate our guess,\n    # the Newton-Raphson method takes more iterations to converge to a solution\n    # because it lacks this precision. A few more calculations now in order to\n    # do fewer calculations later:\n    #   - \"pow = log2(x) // 3\" (the operator `//` means integer division),\n    #   - \"remainder = log2(x) % 3\",\n    #   - \"initial_guess = 2**pow * cbrt(2)**remainder\".\n    # Now substituting \"2 = 1.26 ≈ 1,260 / 1,000\", we get:\n    #   - \"initial_guess = 2**pow * 1,260**remainder // 1,000**remainder\".\n    remainder: uint256 = log2x % 3\n    y: uint256 = unsafe_div(\n        unsafe_mul(pow_mod256(2, unsafe_div(log2x, 3)), pow_mod256(1_260, remainder)), pow_mod256(1_000, remainder)\n    )\n\n    # Since we have chosen good initial values for the cube roots, 7 Newton-Raphson\n    # iterations are just sufficient. 6 iterations would lead to non-convergences,\n    # and 8 would be one iteration too many. Without initial values, the iteration\n    # number can be up to 20 or more. The iterations are unrolled. This reduces the\n    # gas cost, but requires more bytecode.\n    y = unsafe_div(unsafe_add(unsafe_mul(2, y), unsafe_div(value, unsafe_mul(y, y))), 3)\n    y = unsafe_div(unsafe_add(unsafe_mul(2, y), unsafe_div(value, unsafe_mul(y, y))), 3)\n    y = unsafe_div(unsafe_add(unsafe_mul(2, y), unsafe_div(value, unsafe_mul(y, y))), 3)\n    y = unsafe_div(unsafe_add(unsafe_mul(2, y), unsafe_div(value, unsafe_mul(y, y))), 3)\n    y = unsafe_div(unsafe_add(unsafe_mul(2, y), unsafe_div(value, unsafe_mul(y, y))), 3)\n    y = unsafe_div(unsafe_add(unsafe_mul(2, y), unsafe_div(value, unsafe_mul(y, y))), 3)\n    y = unsafe_div(unsafe_add(unsafe_mul(2, y), unsafe_div(value, unsafe_mul(y, y))), 3)\n\n    # Since we scaled up, we have to scale down accordingly.\n    if x >= unsafe_mul(unsafe_div(max_value(uint256), 10 ** 36), 10 ** 18):\n        return unsafe_mul(y, 10 ** 12)\n    elif x >= unsafe_div(max_value(uint256), 10 ** 36):\n        return unsafe_mul(y, 10 ** 6)\n\n    return y\n"}],"certified":false,"conflicting_implementations":null,"abi":[{"anonymous":false,"inputs":[{"indexed":true,"name":"staker","type":"address"}],"name":"SetStaker","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"receiver","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"WithdrawAdminFees","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"allocator","type":"address"},{"indexed":false,"name":"stablecoin_allocation","type":"uint256"},{"indexed":false,"name":"stablecoin_allocated","type":"uint256"}],"name":"AllocateStablecoins","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"min_amount","type":"uint256"},{"indexed":false,"name":"discount","type":"uint256"}],"name":"DistributeBorrowerFees","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"},{"indexed":true,"name":"owner","type":"address"},{"indexed":false,"name":"assets","type":"uint256"},{"indexed":false,"name":"shares","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"},{"indexed":true,"name":"receiver","type":"address"},{"indexed":true,"name":"owner","type":"address"},{"indexed":false,"name":"assets","type":"uint256"},{"indexed":false,"name":"shares","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"admin","type":"address"}],"name":"SetAdmin","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"},{"inputs":[],"name":"min_admin_fee","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"assets","type":"uint256"},{"name":"debt","type":"uint256"}],"name":"preview_deposit","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"tokens","type":"uint256"}],"name":"preview_withdraw","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"assets","type":"uint256"},{"name":"debt","type":"uint256"},{"name":"min_shares","type":"uint256"}],"name":"deposit","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"assets","type":"uint256"},{"name":"debt","type":"uint256"},{"name":"min_shares","type":"uint256"},{"name":"receiver","type":"address"}],"name":"deposit","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"shares","type":"uint256"},{"name":"min_assets","type":"uint256"}],"name":"withdraw","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"shares","type":"uint256"},{"name":"min_assets","type":"uint256"},{"name":"receiver","type":"address"}],"name":"withdraw","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"shares","type":"uint256"}],"name":"preview_emergency_withdraw","outputs":[{"name":"","type":"uint256"},{"name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"shares","type":"uint256"}],"name":"emergency_withdraw","outputs":[{"name":"","type":"uint256"},{"name":"","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"shares","type":"uint256"},{"name":"receiver","type":"address"}],"name":"emergency_withdraw","outputs":[{"name":"","type":"uint256"},{"name":"","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"shares","type":"uint256"},{"name":"receiver","type":"address"},{"name":"owner","type":"address"}],"name":"emergency_withdraw","outputs":[{"name":"","type":"uint256"},{"name":"","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"checkpoint_staker_rebase","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pricePerShare","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"amm","type":"address"}],"name":"set_amm","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"new_admin","type":"address"}],"name":"set_admin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"rate","type":"uint256"}],"name":"set_rate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"fee","type":"uint256"}],"name":"set_amm_fee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"allocate_stablecoins","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"limit","type":"uint256"}],"name":"allocate_stablecoins","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"distribute_borrower_fees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"discount","type":"uint256"}],"name":"distribute_borrower_fees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdraw_admin_fees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"staker","type":"address"}],"name":"set_staker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"is_killed","type":"bool"}],"name":"set_killed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"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":"is_killed","outputs":[{"name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CRYPTOPOOL","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"STABLECOIN","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ASSET_TOKEN","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"admin","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"amm","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"agg","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"staker","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidity","outputs":[{"components":[{"name":"admin","type":"int256"},{"name":"total","type":"uint256"},{"name":"ideal_staked","type":"uint256"},{"name":"staked","type":"uint256"}],"name":"","type":"tuple"}],"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":"arg0","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stablecoin_allocation","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stablecoin_allocated","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"asset_token","type":"address"},{"name":"stablecoin","type":"address"},{"name":"cryptopool","type":"address"},{"name":"admin","type":"address"}],"outputs":[],"stateMutability":"nonpayable","type":"constructor"}],"is_changed_bytecode":false,"is_partially_verified":true,"constructor_args":"0x0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c599000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e000000000000000000000000d9ff8396554a0d18b2cfbec53e1979b7ecce8373000000000000000000000000370a449febb9411c95bf897021377fe0b7d100c0"}