Code for 0xa5dc…d207 (LiquidityGaugeV6)
Since block 19409361
Verified contract
- # pragma version 0.3.10
- # pragma optimize gas
- # pragma evm-version shanghai
- """
- @title LiquidityGaugeV6
- @author Curve.Fi
- @license Copyright (c) Curve.Fi, 2020-2023 - all rights reserved
- @notice Implementation contract for use with Curve Factory
- @dev Differs from v5.0.0 in that it uses create_from_blueprint to deploy Gauges
- """
- from vyper.interfaces import ERC20
-
- implements: ERC20
-
-
- interface CRV20:
- def future_epoch_time_write() -> uint256: nonpayable
- def rate() -> uint256: view
-
- interface Controller:
- def checkpoint_gauge(addr: address): nonpayable
- def gauge_relative_weight(addr: address, time: uint256) -> uint256: view
-
- interface ERC20Extended:
- def symbol() -> String[32]: view
-
- interface ERC1271:
- def isValidSignature(_hash: bytes32, _signature: Bytes[65]) -> bytes32: view
-
- interface Factory:
- def admin() -> address: view
-
- interface Minter:
- def minted(user: address, gauge: address) -> uint256: view
-
- interface VotingEscrow:
- def user_point_epoch(addr: address) -> uint256: view
- def user_point_history__ts(addr: address, epoch: uint256) -> uint256: view
-
- interface VotingEscrowBoost:
- def adjusted_balance_of(_account: address) -> uint256: view
-
-
- event Deposit:
- provider: indexed(address)
- value: uint256
-
- event Withdraw:
- provider: indexed(address)
- value: uint256
-
- event UpdateLiquidityLimit:
- user: indexed(address)
- original_balance: uint256
- original_supply: uint256
- working_balance: uint256
- working_supply: uint256
-
- event CommitOwnership:
- admin: address
-
- event ApplyOwnership:
- admin: address
-
- event SetGaugeManager:
- _gauge_manager: address
-
-
- event Transfer:
- _from: indexed(address)
- _to: indexed(address)
- _value: uint256
-
- event Approval:
- _owner: indexed(address)
- _spender: indexed(address)
- _value: uint256
-
-
- struct Reward:
- token: address
- distributor: address
- period_finish: uint256
- rate: uint256
- last_update: uint256
- integral: uint256
-
-
- MAX_REWARDS: constant(uint256) = 8
- TOKENLESS_PRODUCTION: constant(uint256) = 40
- WEEK: constant(uint256) = 604800
-
- VERSION: constant(String[8]) = "v6.1.0" # <- updated from v6.0.0 (makes rewards semi-permissionless)
-
- EIP712_TYPEHASH: constant(bytes32) = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
- EIP2612_TYPEHASH: constant(bytes32) = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")
-
- VERSION_HASH: constant(bytes32) = keccak256(VERSION)
- NAME_HASH: immutable(bytes32)
- CACHED_CHAIN_ID: immutable(uint256)
- salt: public(immutable(bytes32))
- CACHED_DOMAIN_SEPARATOR: immutable(bytes32)
-
- CRV: constant(address) = 0xD533a949740bb3306d119CC777fa900bA034cd52
- GAUGE_CONTROLLER: constant(address) = 0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB
- MINTER: constant(address) = 0xd061D61a4d941c39E5453435B6345Dc261C2fcE0
- VEBOOST_PROXY: constant(address) = 0x8E0c00ed546602fD9927DF742bbAbF726D5B0d16
- VOTING_ESCROW: constant(address) = 0x5f3b5DfEb7B28CDbD7FAba78963EE202a494e2A2
-
-
- # ERC20
- balanceOf: public(HashMap[address, uint256])
- totalSupply: public(uint256)
- allowance: public(HashMap[address, HashMap[address, uint256]])
-
- name: public(String[64])
- symbol: public(String[40])
-
- # ERC2612
- nonces: public(HashMap[address, uint256])
-
- # Gauge
- factory: public(address)
- manager: public(address)
- lp_token: public(address)
-
- is_killed: public(bool)
-
- # [future_epoch_time uint40][inflation_rate uint216]
- inflation_params: uint256
-
- # For tracking external rewards
- reward_count: public(uint256)
- reward_data: public(HashMap[address, Reward])
-
- # claimant -> default reward receiver
- rewards_receiver: public(HashMap[address, address])
-
- # reward token -> claiming address -> integral
- reward_integral_for: public(HashMap[address, HashMap[address, uint256]])
-
- # user -> [uint128 claimable amount][uint128 claimed amount]
- claim_data: HashMap[address, HashMap[address, uint256]]
-
- working_balances: public(HashMap[address, uint256])
- working_supply: public(uint256)
-
- # 1e18 * ∫(rate(t) / totalSupply(t) dt) from (last_action) till checkpoint
- integrate_inv_supply_of: public(HashMap[address, uint256])
- integrate_checkpoint_of: public(HashMap[address, uint256])
-
- # ∫(balance * rate(t) / totalSupply(t) dt) from 0 till checkpoint
- # Units: rate * t = already number of coins per address to issue
- integrate_fraction: public(HashMap[address, uint256])
-
- # The goal is to be able to calculate ∫(rate * balance / totalSupply dt) from 0 till checkpoint
- # All values are kept in units of being multiplied by 1e18
- period: public(int128)
-
- # array of reward tokens
- reward_tokens: public(address[MAX_REWARDS])
-
- period_timestamp: public(uint256[100000000000000000000000000000])
- # 1e18 * ∫(rate(t) / totalSupply(t) dt) from 0 till checkpoint
- integrate_inv_supply: public(uint256[100000000000000000000000000000]) # bump epoch when rate() changes
-
-
- @external
- def __init__(_lp_token: address):
- """
- @notice Contract constructor
- @param _lp_token Liquidity Pool contract address
- """
- self.lp_token = _lp_token
- self.factory = msg.sender
- self.manager = tx.origin
-
- symbol: String[32] = ERC20Extended(_lp_token).symbol()
- name: String[64] = concat("Curve.fi ", symbol, " Gauge Deposit")
-
- self.name = name
- self.symbol = concat(symbol, "-gauge")
-
- self.period_timestamp[0] = block.timestamp
- self.inflation_params = (
- (CRV20(CRV).future_epoch_time_write() << 216)
- + CRV20(CRV).rate()
- )
-
- NAME_HASH = keccak256(name)
- salt = block.prevhash
- CACHED_CHAIN_ID = chain.id
- CACHED_DOMAIN_SEPARATOR = keccak256(
- _abi_encode(
- EIP712_TYPEHASH,
- NAME_HASH,
- VERSION_HASH,
- chain.id,
- self,
- salt,
- )
- )
-
-
- # Internal Functions
-
- @view
- @internal
- def _domain_separator() -> bytes32:
- if chain.id != CACHED_CHAIN_ID:
- return keccak256(
- _abi_encode(
- EIP712_TYPEHASH,
- NAME_HASH,
- VERSION_HASH,
- chain.id,
- self,
- salt,
- )
- )
- return CACHED_DOMAIN_SEPARATOR
-
-
- @internal
- def _checkpoint(addr: address):
- """
- @notice Checkpoint for a user
- @dev Updates the CRV emissions a user is entitled to receive
- @param addr User address
- """
- _period: int128 = self.period
- _period_time: uint256 = self.period_timestamp[_period]
- _integrate_inv_supply: uint256 = self.integrate_inv_supply[_period]
-
- inflation_params: uint256 = self.inflation_params
- prev_future_epoch: uint256 = inflation_params >> 216
- gauge_is_killed: bool = self.is_killed
-
- rate: uint256 = inflation_params % 2 ** 216
- new_rate: uint256 = rate
- if gauge_is_killed:
- rate = 0
- new_rate = 0
-
- if prev_future_epoch >= _period_time:
- future_epoch_time_write: uint256 = CRV20(CRV).future_epoch_time_write()
- if not gauge_is_killed:
- new_rate = CRV20(CRV).rate()
- self.inflation_params = (future_epoch_time_write << 216) + new_rate
-
- # Update integral of 1/supply
- if block.timestamp > _period_time:
- _working_supply: uint256 = self.working_supply
- Controller(GAUGE_CONTROLLER).checkpoint_gauge(self)
- prev_week_time: uint256 = _period_time
- week_time: uint256 = min((_period_time + WEEK) / WEEK * WEEK, block.timestamp)
-
- for i in range(500):
- dt: uint256 = week_time - prev_week_time
- w: uint256 = Controller(GAUGE_CONTROLLER).gauge_relative_weight(self, prev_week_time)
-
- if _working_supply > 0:
- if prev_future_epoch >= prev_week_time and prev_future_epoch < week_time:
- # If we went across one or multiple epochs, apply the rate
- # of the first epoch until it ends, and then the rate of
- # the last epoch.
- # If more than one epoch is crossed - the gauge gets less,
- # but that'd meen it wasn't called for more than 1 year
- _integrate_inv_supply += rate * w * (prev_future_epoch - prev_week_time) / _working_supply
- rate = new_rate
- _integrate_inv_supply += rate * w * (week_time - prev_future_epoch) / _working_supply
- else:
- _integrate_inv_supply += rate * w * dt / _working_supply
- # On precisions of the calculation
- # rate ~= 10e18
- # last_weight > 0.01 * 1e18 = 1e16 (if pool weight is 1%)
- # _working_supply ~= TVL * 1e18 ~= 1e26 ($100M for example)
- # The largest loss is at dt = 1
- # Loss is 1e-9 - acceptable
-
- if week_time == block.timestamp:
- break
- prev_week_time = week_time
- week_time = min(week_time + WEEK, block.timestamp)
-
- _period += 1
- self.period = _period
- self.period_timestamp[_period] = block.timestamp
- self.integrate_inv_supply[_period] = _integrate_inv_supply
-
- # Update user-specific integrals
- _working_balance: uint256 = self.working_balances[addr]
- self.integrate_fraction[addr] += _working_balance * (_integrate_inv_supply - self.integrate_inv_supply_of[addr]) / 10 ** 18
- self.integrate_inv_supply_of[addr] = _integrate_inv_supply
- self.integrate_checkpoint_of[addr] = block.timestamp
-
-
- @internal
- def _checkpoint_rewards(_user: address, _total_supply: uint256, _claim: bool, _receiver: address):
- """
- @notice Claim pending rewards and checkpoint rewards for a user
- """
-
- user_balance: uint256 = 0
- receiver: address = _receiver
- if _user != empty(address):
- user_balance = self.balanceOf[_user]
- if _claim and _receiver == empty(address):
- # if receiver is not explicitly declared, check if a default receiver is set
- receiver = self.rewards_receiver[_user]
- if receiver == empty(address):
- # if no default receiver is set, direct claims to the user
- receiver = _user
-
- reward_count: uint256 = self.reward_count
- for i in range(MAX_REWARDS):
- if i == reward_count:
- break
- token: address = self.reward_tokens[i]
-
- integral: uint256 = self.reward_data[token].integral
- last_update: uint256 = min(block.timestamp, self.reward_data[token].period_finish)
- duration: uint256 = last_update - self.reward_data[token].last_update
-
- if duration != 0 and _total_supply != 0:
- self.reward_data[token].last_update = last_update
- integral += duration * self.reward_data[token].rate * 10**18 / _total_supply
- self.reward_data[token].integral = integral
-
- if _user != empty(address):
- integral_for: uint256 = self.reward_integral_for[token][_user]
- new_claimable: uint256 = 0
-
- if integral_for < integral:
- self.reward_integral_for[token][_user] = integral
- new_claimable = user_balance * (integral - integral_for) / 10**18
-
- claim_data: uint256 = self.claim_data[_user][token]
- total_claimable: uint256 = (claim_data >> 128) + new_claimable
- if total_claimable > 0:
- total_claimed: uint256 = claim_data % 2**128
- if _claim:
- assert ERC20(token).transfer(receiver, total_claimable, default_return_value=True)
- self.claim_data[_user][token] = total_claimed + total_claimable
- elif new_claimable > 0:
- self.claim_data[_user][token] = total_claimed + (total_claimable << 128)
-
-
- @internal
- def _update_liquidity_limit(addr: address, l: uint256, L: uint256):
- """
- @notice Calculate limits which depend on the amount of CRV token per-user.
- Effectively it calculates working balances to apply amplification
- of CRV production by CRV
- @param addr User address
- @param l User's amount of liquidity (LP tokens)
- @param L Total amount of liquidity (LP tokens)
- """
- # To be called after totalSupply is updated
- voting_balance: uint256 = VotingEscrowBoost(VEBOOST_PROXY).adjusted_balance_of(addr)
- voting_total: uint256 = ERC20(VOTING_ESCROW).totalSupply()
-
- lim: uint256 = l * TOKENLESS_PRODUCTION / 100
- if voting_total > 0:
- lim += L * voting_balance / voting_total * (100 - TOKENLESS_PRODUCTION) / 100
-
- lim = min(l, lim)
- old_bal: uint256 = self.working_balances[addr]
- self.working_balances[addr] = lim
- _working_supply: uint256 = self.working_supply + lim - old_bal
- self.working_supply = _working_supply
-
- log UpdateLiquidityLimit(addr, l, L, lim, _working_supply)
-
-
- @internal
- def _transfer(_from: address, _to: address, _value: uint256):
- """
- @notice Transfer tokens as well as checkpoint users
- """
- self._checkpoint(_from)
- self._checkpoint(_to)
-
- if _value != 0:
- total_supply: uint256 = self.totalSupply
- is_rewards: bool = self.reward_count != 0
- if is_rewards:
- self._checkpoint_rewards(_from, total_supply, False, empty(address))
- new_balance: uint256 = self.balanceOf[_from] - _value
- self.balanceOf[_from] = new_balance
- self._update_liquidity_limit(_from, new_balance, total_supply)
-
- if is_rewards:
- self._checkpoint_rewards(_to, total_supply, False, empty(address))
- new_balance = self.balanceOf[_to] + _value
- self.balanceOf[_to] = new_balance
- self._update_liquidity_limit(_to, new_balance, total_supply)
-
- log Transfer(_from, _to, _value)
-
-
- # External User Facing Functions
-
-
- @external
- @nonreentrant('lock')
- def deposit(_value: uint256, _addr: address = msg.sender, _claim_rewards: bool = False):
- """
- @notice Deposit `_value` LP tokens
- @dev Depositting also claims pending reward tokens
- @param _value Number of tokens to deposit
- @param _addr Address to deposit for
- """
- assert _addr != empty(address) # dev: cannot deposit for zero address
- self._checkpoint(_addr)
-
- if _value != 0:
- is_rewards: bool = self.reward_count != 0
- total_supply: uint256 = self.totalSupply
- if is_rewards:
- self._checkpoint_rewards(_addr, total_supply, _claim_rewards, empty(address))
-
- total_supply += _value
- new_balance: uint256 = self.balanceOf[_addr] + _value
- self.balanceOf[_addr] = new_balance
- self.totalSupply = total_supply
-
- self._update_liquidity_limit(_addr, new_balance, total_supply)
-
- ERC20(self.lp_token).transferFrom(msg.sender, self, _value)
-
- log Deposit(_addr, _value)
- log Transfer(empty(address), _addr, _value)
-
-
- @external
- @nonreentrant('lock')
- def withdraw(_value: uint256, _claim_rewards: bool = False):
- """
- @notice Withdraw `_value` LP tokens
- @dev Withdrawing also claims pending reward tokens
- @param _value Number of tokens to withdraw
- """
- self._checkpoint(msg.sender)
-
- if _value != 0:
- is_rewards: bool = self.reward_count != 0
- total_supply: uint256 = self.totalSupply
- if is_rewards:
- self._checkpoint_rewards(msg.sender, total_supply, _claim_rewards, empty(address))
-
- total_supply -= _value
- new_balance: uint256 = self.balanceOf[msg.sender] - _value
- self.balanceOf[msg.sender] = new_balance
- self.totalSupply = total_supply
-
- self._update_liquidity_limit(msg.sender, new_balance, total_supply)
-
- ERC20(self.lp_token).transfer(msg.sender, _value)
-
- log Withdraw(msg.sender, _value)
- log Transfer(msg.sender, empty(address), _value)
-
-
- @external
- @nonreentrant('lock')
- def claim_rewards(_addr: address = msg.sender, _receiver: address = empty(address)):
- """
- @notice Claim available reward tokens for `_addr`
- @param _addr Address to claim for
- @param _receiver Address to transfer rewards to - if set to
- empty(address), uses the default reward receiver
- for the caller
- """
- if _receiver != empty(address):
- assert _addr == msg.sender # dev: cannot redirect when claiming for another user
- self._checkpoint_rewards(_addr, self.totalSupply, True, _receiver)
-
-
- @external
- @nonreentrant('lock')
- def transferFrom(_from: address, _to :address, _value: uint256) -> bool:
- """
- @notice Transfer tokens from one address to another.
- @dev Transferring claims pending reward tokens for the sender and receiver
- @param _from address The address which you want to send tokens from
- @param _to address The address which you want to transfer to
- @param _value uint256 the amount of tokens to be transferred
- """
- _allowance: uint256 = self.allowance[_from][msg.sender]
- if _allowance != max_value(uint256):
- self.allowance[_from][msg.sender] = _allowance - _value
-
- self._transfer(_from, _to, _value)
-
- return True
-
-
- @external
- @nonreentrant('lock')
- def transfer(_to: address, _value: uint256) -> bool:
- """
- @notice Transfer token for a specified address
- @dev Transferring claims pending reward tokens for the sender and receiver
- @param _to The address to transfer to.
- @param _value The amount to be transferred.
- """
- self._transfer(msg.sender, _to, _value)
-
- return True
-
-
- @external
- def approve(_spender : address, _value : uint256) -> bool:
- """
- @notice Approve the passed address to transfer the specified amount of
- tokens on behalf of msg.sender
- @dev Beware that changing an allowance via this method brings the risk
- that someone may use both the old and new allowance by unfortunate
- transaction ordering. This may be mitigated with the use of
- {incraseAllowance} and {decreaseAllowance}.
- https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
- @param _spender The address which will transfer the funds
- @param _value The amount of tokens that may be transferred
- @return bool success
- """
- self.allowance[msg.sender][_spender] = _value
- log Approval(msg.sender, _spender, _value)
-
- return True
-
-
- @external
- def permit(
- _owner: address,
- _spender: address,
- _value: uint256,
- _deadline: uint256,
- _v: uint8,
- _r: bytes32,
- _s: bytes32
- ) -> bool:
- """
- @notice Approves spender by owner's signature to expend owner's tokens.
- See https://eips.ethereum.org/EIPS/eip-2612.
- @dev Inspired by https://github.com/yearn/yearn-vaults/blob/main/contracts/Vault.vy#L753-L793
- @dev Supports smart contract wallets which implement ERC1271
- https://eips.ethereum.org/EIPS/eip-1271
- @param _owner The address which is a source of funds and has signed the Permit.
- @param _spender The address which is allowed to spend the funds.
- @param _value The amount of tokens to be spent.
- @param _deadline The timestamp after which the Permit is no longer valid.
- @param _v The bytes[64] of the valid secp256k1 signature of permit by owner
- @param _r The bytes[0:32] of the valid secp256k1 signature of permit by owner
- @param _s The bytes[32:64] of the valid secp256k1 signature of permit by owner
- @return True, if transaction completes successfully
- """
- assert _owner != empty(address) # dev: invalid owner
- assert block.timestamp <= _deadline # dev: permit expired
-
- nonce: uint256 = self.nonces[_owner]
- digest: bytes32 = keccak256(
- concat(
- b"\x19\x01",
- self._domain_separator(),
- keccak256(
- _abi_encode(
- EIP2612_TYPEHASH, _owner, _spender, _value, nonce, _deadline
- )
- ),
- )
- )
- assert ecrecover(digest, _v, _r, _s) == _owner # dev: invalid signature
-
- self.allowance[_owner][_spender] = _value
- self.nonces[_owner] = unsafe_add(nonce, 1)
-
- log Approval(_owner, _spender, _value)
- return True
-
-
- @external
- def increaseAllowance(_spender: address, _added_value: uint256) -> bool:
- """
- @notice Increase the allowance granted to `_spender` by the caller
- @dev This is alternative to {approve} that can be used as a mitigation for
- the potential race condition
- @param _spender The address which will transfer the funds
- @param _added_value The amount of to increase the allowance
- @return bool success
- """
- allowance: uint256 = self.allowance[msg.sender][_spender] + _added_value
- self.allowance[msg.sender][_spender] = allowance
-
- log Approval(msg.sender, _spender, allowance)
-
- return True
-
-
- @external
- def decreaseAllowance(_spender: address, _subtracted_value: uint256) -> bool:
- """
- @notice Decrease the allowance granted to `_spender` by the caller
- @dev This is alternative to {approve} that can be used as a mitigation for
- the potential race condition
- @param _spender The address which will transfer the funds
- @param _subtracted_value The amount of to decrease the allowance
- @return bool success
- """
- allowance: uint256 = self.allowance[msg.sender][_spender] - _subtracted_value
- self.allowance[msg.sender][_spender] = allowance
-
- log Approval(msg.sender, _spender, allowance)
-
- return True
-
-
- @external
- def user_checkpoint(addr: address) -> bool:
- """
- @notice Record a checkpoint for `addr`
- @param addr User address
- @return bool success
- """
- assert msg.sender in [addr, MINTER] # dev: unauthorized
- self._checkpoint(addr)
- self._update_liquidity_limit(addr, self.balanceOf[addr], self.totalSupply)
- return True
-
-
- @external
- def set_rewards_receiver(_receiver: address):
- """
- @notice Set the default reward receiver for the caller.
- @dev When set to empty(address), rewards are sent to the caller
- @param _receiver Receiver address for any rewards claimed via `claim_rewards`
- """
- self.rewards_receiver[msg.sender] = _receiver
-
-
- @external
- def kick(addr: address):
- """
- @notice Kick `addr` for abusing their boost
- @dev Only if either they had another voting event, or their voting escrow lock expired
- @param addr Address to kick
- """
- t_last: uint256 = self.integrate_checkpoint_of[addr]
- t_ve: uint256 = VotingEscrow(VOTING_ESCROW).user_point_history__ts(
- addr, VotingEscrow(VOTING_ESCROW).user_point_epoch(addr)
- )
- _balance: uint256 = self.balanceOf[addr]
-
- assert ERC20(VOTING_ESCROW).balanceOf(addr) == 0 or t_ve > t_last # dev: kick not allowed
- assert self.working_balances[addr] > _balance * TOKENLESS_PRODUCTION / 100 # dev: kick not needed
-
- self._checkpoint(addr)
- self._update_liquidity_limit(addr, self.balanceOf[addr], self.totalSupply)
-
-
- # Administrative Functions
-
-
- @external
- def set_gauge_manager(_gauge_manager: address):
- """
- @notice Change the gauge manager for a gauge
- @dev The manager of this contract, or the ownership admin can outright modify gauge
- managership. A gauge manager can also transfer managership to a new manager via this
- method, but only for the gauge which they are the manager of.
- @param _gauge_manager The account to set as the new manager of the gauge.
- """
- assert msg.sender in [self.manager, Factory(self.factory).admin()] # dev: only manager or factory admin
-
- self.manager = _gauge_manager
- log SetGaugeManager(_gauge_manager)
-
-
- @external
- @nonreentrant("lock")
- def deposit_reward_token(_reward_token: address, _amount: uint256, _epoch: uint256 = WEEK):
- """
- @notice Deposit a reward token for distribution
- @param _reward_token The reward token being deposited
- @param _amount The amount of `_reward_token` being deposited
- @param _epoch The duration the rewards are distributed across.
- """
- assert msg.sender == self.reward_data[_reward_token].distributor
-
- self._checkpoint_rewards(empty(address), self.totalSupply, False, empty(address))
-
- # transferFrom reward token and use transferred amount henceforth:
- amount_received: uint256 = ERC20(_reward_token).balanceOf(self)
- assert ERC20(_reward_token).transferFrom(
- msg.sender,
- self,
- _amount,
- default_return_value=True
- )
- amount_received = ERC20(_reward_token).balanceOf(self) - amount_received
-
- period_finish: uint256 = self.reward_data[_reward_token].period_finish
- assert amount_received > _epoch # dev: rate will tend to zero!
-
- if block.timestamp >= period_finish:
- self.reward_data[_reward_token].rate = amount_received / _epoch
- else:
- remaining: uint256 = period_finish - block.timestamp
- leftover: uint256 = remaining * self.reward_data[_reward_token].rate
- self.reward_data[_reward_token].rate = (amount_received + leftover) / _epoch
-
- self.reward_data[_reward_token].last_update = block.timestamp
- self.reward_data[_reward_token].period_finish = block.timestamp + _epoch
-
-
- @external
- def add_reward(_reward_token: address, _distributor: address):
- """
- @notice Add additional rewards to be distributed to stakers
- @param _reward_token The token to add as an additional reward
- @param _distributor Address permitted to fund this contract with the reward token
- """
- assert msg.sender in [self.manager, Factory(self.factory).admin()] # dev: only manager or factory admin
- assert _distributor != empty(address) # dev: distributor cannot be zero address
-
- reward_count: uint256 = self.reward_count
- assert reward_count < MAX_REWARDS
- assert self.reward_data[_reward_token].distributor == empty(address)
-
- self.reward_data[_reward_token].distributor = _distributor
- self.reward_tokens[reward_count] = _reward_token
- self.reward_count = reward_count + 1
-
-
- @external
- def set_reward_distributor(_reward_token: address, _distributor: address):
- """
- @notice Reassign the reward distributor for a reward token
- @param _reward_token The reward token to reassign distribution rights to
- @param _distributor The address of the new distributor
- """
- current_distributor: address = self.reward_data[_reward_token].distributor
-
- assert msg.sender in [current_distributor, Factory(self.factory).admin(), self.manager]
- assert current_distributor != empty(address)
- assert _distributor != empty(address)
-
- self.reward_data[_reward_token].distributor = _distributor
-
-
- @external
- def set_killed(_is_killed: bool):
- """
- @notice Set the killed status for this contract
- @dev When killed, the gauge always yields a rate of 0 and so cannot mint CRV
- @param _is_killed Killed status to set
- """
- assert msg.sender == Factory(self.factory).admin() # dev: only owner
-
- self.is_killed = _is_killed
-
-
- # View Methods
-
-
- @view
- @external
- def claimed_reward(_addr: address, _token: address) -> uint256:
- """
- @notice Get the number of already-claimed reward tokens for a user
- @param _addr Account to get reward amount for
- @param _token Token to get reward amount for
- @return uint256 Total amount of `_token` already claimed by `_addr`
- """
- return self.claim_data[_addr][_token] % 2**128
-
-
- @view
- @external
- def claimable_reward(_user: address, _reward_token: address) -> uint256:
- """
- @notice Get the number of claimable reward tokens for a user
- @param _user Account to get reward amount for
- @param _reward_token Token to get reward amount for
- @return uint256 Claimable reward token amount
- """
- integral: uint256 = self.reward_data[_reward_token].integral
- total_supply: uint256 = self.totalSupply
- if total_supply != 0:
- last_update: uint256 = min(block.timestamp, self.reward_data[_reward_token].period_finish)
- duration: uint256 = last_update - self.reward_data[_reward_token].last_update
- integral += (duration * self.reward_data[_reward_token].rate * 10**18 / total_supply)
-
- integral_for: uint256 = self.reward_integral_for[_reward_token][_user]
- new_claimable: uint256 = self.balanceOf[_user] * (integral - integral_for) / 10**18
-
- return (self.claim_data[_user][_reward_token] >> 128) + new_claimable
-
-
- @external
- def claimable_tokens(addr: address) -> uint256:
- """
- @notice Get the number of claimable tokens per user
- @dev This function should be manually changed to "view" in the ABI
- @return uint256 number of claimable tokens per user
- """
- self._checkpoint(addr)
- return self.integrate_fraction[addr] - Minter(MINTER).minted(addr, self)
-
-
- @view
- @external
- def integrate_checkpoint() -> uint256:
- """
- @notice Get the timestamp of the last checkpoint
- """
- return self.period_timestamp[self.period]
-
-
- @view
- @external
- def future_epoch_time() -> uint256:
- """
- @notice Get the locally stored CRV future epoch start time
- """
- return self.inflation_params >> 216
-
-
- @view
- @external
- def inflation_rate() -> uint256:
- """
- @notice Get the locally stored CRV inflation rate
- """
- return self.inflation_params % 2 ** 216
-
-
- @view
- @external
- def decimals() -> uint256:
- """
- @notice Get the number of decimals for this token
- @dev Implemented as a view method to reduce gas costs
- @return uint256 decimal places
- """
- return 18
-
-
- @view
- @external
- def version() -> String[8]:
- """
- @notice Get the version of this gauge contract
- """
- return VERSION
-
-
- @view
- @external
- def DOMAIN_SEPARATOR() -> bytes32:
- """
- @notice EIP712 domain separator.
- """
- return self._domain_separator()
Contract sourced from Etherscan. Solidity version vyper:0.3.10
.
Panoramix decompilation
# Palkeoramix decompiler. def _fallback(?) payable: # default function revert
Decompilation generated by Panoramix.
Raw bytecode
0x5f3560e01c6002603f821660011b6128e501601e395f51565b63bfa0b133811861003657346128e15760206129a560403960206040f35b63095ea7b38118611c2d576044361034176128e1576004358060a01c6128e1576040526024356003336020525f5260405f20806040516020525f5260405f20905055604051337f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560243560605260206060a3600160605260206060f3611c2d565b6370a0823181186100f2576024361034176128e1576004358060a01c6128e15760405260016040516020525f5260405f205460605260206060f35b6301ddabf18118611c2d576024361034176128e1576004358060a01c6128e15760405260126040516020525f5260405f205460605260206060f3611c2d565b6318160ddd811861014d57346128e15760025460405260206040f35b6323b872dd8118611c2d576064361034176128e1576004358060a01c6128e1576103a0526024358060a01c6128e1576103c0525f546002146128e15760025f5560036103a0516020525f5260405f2080336020525f5260405f209050546103e0527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6103e0511461020b576103e0516044358082038281116128e1579050905060036103a0516020525f5260405f2080336020525f5260405f209050555b6103a0516102e0526103c051610300526044356103205261022a612779565b600161040052602061040060035f55f3611c2d565b63dd62ed3e8118610297576044361034176128e1576004358060a01c6128e1576040526024358060a01c6128e15760605260036040516020525f5260405f20806060516020525f5260405f2090505460805260206080f35b6384e9bd7e8118611c2d576024361034176128e1576004358060a01c6128e1576102e0525f61030052610f3a56611c2d565b6306fdde03811861034457346128e157602080604052806040016020600454015f81601f0160051c600381116128e157801561031857905b80600401548160051b850152600101818118610301575b5050508051806020830101601f825f03163682375050601f19601f825160200101169050810190506040f35b63331345838118611c2d576024361034176128e1576004358060a01c6128e1576102805261028051604052610377611cc4565b6019610280516020525f5260405f2054638b752bb06102a052610280516102c052306102e05260206102a060446102bc73d061d61a4d941c39e5453435b6345dc261c2fce05afa6103ca573d5f5f3e3d5ffd5b60203d106128e1576102a0518082038281116128e15790509050610300526020610300f3611c2d565b6395d89b418118611c2d57346128e157602080604052806040016020600754015f81601f0160051c600381116128e157801561044257905b80600701548160051b85015260010181811861042b575b5050508051806020830101601f825f03163682375050601f19601f825160200101169050810190506040f3611c2d565b637ecebe0081146003361116156104b3576024361034176128e1576004358060a01c6128e157604052600a6040516020525f5260405f205460605260206060f35b639c868ac08118611c2d57346128e157600e5460405260206040f3611c2d565b63c45a015581186104ef57346128e157600b5460405260206040f35b633644e5158118611c2d57346128e157602061050c610120611c31565b610120f3611c2d565b63481c6a75811861053157346128e157600c5460405260206040f35b6396c551758118611c2d576024361034176128e1576004358060a01c6128e157610280526018610280516020525f5260405f20546102a05263da020a1861032052610280516103405263010ae7576102e052610280516103005260206102e060246102fc735f3b5dfeb7b28cdbd7faba78963ee202a494e2a25afa6105b8573d5f5f3e3d5ffd5b60203d106128e1576102e051610360526020610320604461033c735f3b5dfeb7b28cdbd7faba78963ee202a494e2a25afa6105f5573d5f5f3e3d5ffd5b60203d106128e157610320516102c0526001610280516020525f5260405f20546102e0526370a082316103005261028051610320526020610300602461031c735f3b5dfeb7b28cdbd7faba78963ee202a494e2a25afa610657573d5f5f3e3d5ffd5b60203d106128e1576103005161066e576001610678565b6102a0516102c051115b156128e1576102e051602881028160288204186128e15790506064810490506015610280516020525f5260405f205411156128e157610280516040526106bc611cc4565b610280516040526001610280516020525f5260405f20546060526002546080526106e46125c3565b00611c2d565b6382c630668118611c2d57346128e157600d5460405260206040f3611c2d565b63963c94b98118611c2d57346128e15760105460405260206040f3611c2d565b6348e9c65e8118611c2d576024361034176128e1576004358060a01c6128e15760405260116040516020525f5260405f2080546060526001810154608052600281015460a052600381015460c052600481015460e0526005810154610100525060c06060f3611c2d565b63f05cc0588118611c2d576044361034176128e1576004358060a01c6128e1576040526024358060a01c6128e15760605260136040516020525f5260405f20806060516020525f5260405f2090505460805260206080f3611c2d565b6313ecb1ca8118611c2d576024361034176128e1576004358060a01c6128e15760405260156040516020525f5260405f205460605260206060f3611c2d565b6317e280898118611c2d57346128e15760165460405260206040f3611c2d565b63de263bfa8118611c2d576024361034176128e1576004358060a01c6128e15760405260176040516020525f5260405f205460605260206060f3611c2d565b639bd324f281186108c9576024361034176128e1576004358060a01c6128e15760405260186040516020525f5260405f205460605260206060f35b63e6f1daf28118611c2d57346128e157336102e0525f61030052610f3a56611c2d565b63094007078118610927576024361034176128e1576004358060a01c6128e15760405260196040516020525f5260405f205460605260206060f35b6383df67478118611c2d576064361034176128e1576024358060a01c6128e1576102e0526044358060011c6128e157610300525b5f546002146128e15760025f556102e051156128e1576102e051604052610980611cc4565b60043515610af2576010541515610320526002546103405261032051156109c2576102e05160405261034051606052610300516080525f60a0526109c26121f9565b610340516004358082018281106128e157905090506103405260016102e0516020525f5260405f20546004358082018281106128e15790509050610360526103605160016102e0516020525f5260405f2055610340516002556102e0516040526103605160605261034051608052610a386125c3565b600d546323b872dd61038052336103a052306103c0526004356103e0526020610380606461039c5f855af1610a6f573d5f5f3e3d5ffd5b60203d106128e157610380518060011c6128e1576104005261040050506102e0517fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c600435610380526020610380a26102e0515f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef600435610380526020610380a35b60035f5500611c2d565b63ef78d4fd8118611c2d57346128e157601a5460405260206040f3611c2d565b6354c49fe98118610b49576024361034176128e157600435600781116128e157601b015460405260206040f35b63be5d1be98118611c2d57346128e157600f5460d81c60405260206040f3611c2d565b637598108c8118610ba5576024361034176128e1576004356c01431e0fae6d7217ca9fffffff81116128e1576023015460405260206040f35b63fec8ee0c8118611c2d576024361034176128e1576004356c01431e0fae6d7217ca9fffffff81116128e1576c01431e0fae6d7217caa0000023015460405260206040f3611c2d565b63b6b55f258118610c12576024361034176128e157336102e0525f6103005261095b565b636e553f658118611c2d576044361034176128e1576024358060a01c6128e1576102e0525f6103005261095b56611c2d565b632e1a7d4d8118610c63576024361034176128e1575f6102e052610d88565b63e8de0d4d8118611c2d576044361034176128e1576004358060a01c6128e1576040526024358060a01c6128e15760605233600c548118610ca5576001610ce8565b600b5463f851a44060e052602060e0600460fc845afa610cc7573d5f5f3e3d5ffd5b60203d106128e15760e0518060a01c6128e157610120526101209050518118155b9050156128e157606051156128e1576010546080526007608051116128e15760116040516020525f5260405f20600181019050546128e15760605160116040516020525f5260405f2060018101905055604051608051600781116128e157601b0155608051600181018181106128e157905060105500611c2d565b6338d074368118611c2d576044361034176128e1576024358060011c6128e1576102e0525b5f546002146128e15760025f5533604052610da1611cc4565b60043515610e9d57601054151561030052600254610320526103005115610de05733604052610320516060526102e0516080525f60a052610de06121f9565b610320516004358082038281116128e15790509050610320526001336020525f5260405f20546004358082038281116128e1579050905061034052610340516001336020525f5260405f205561032051600255336040526103405160605261032051608052610e4d6125c3565b600d5463a9059cbb6103605233610380526004356103a0526020610360604461037c5f855af1610e7f573d5f5f3e3d5ffd5b60203d106128e157610360518060011c6128e1576103c0526103c050505b337f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364600435610300526020610300a25f337fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef600435610300526020610300a360035f5500611c2d565b639faceb1b8118611c2d576044361034176128e1576004358060a01c6128e1576102e0526024358060a01c6128e157610300525b5f546002146128e15760025f556103005115610f5b57336102e051186128e1575b6102e05160405260025460605260016080526103005160a052610f7c6121f9565b60035f5500611c2d565b63a9059cbb8118611c2d576044361034176128e1576004358060a01c6128e1576103a0525f546002146128e15760025f55336102e0526103a0516103005260243561032052610fd3612779565b60016103c05260206103c060035f55f3611c2d565b63d505accf8118611c2d5760e4361034176128e1576004358060a01c6128e157610120526024358060a01c6128e157610140526084358060081c6128e1576101605261012051156128e15760643542116128e157600a610120516020525f5260405f2054610180525f60026101c0527f19010000000000000000000000000000000000000000000000000000000000006101e0526101c08051602082018361032001815181525050808301925050506110a2610200611c31565b610200518161032001526020810190507f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c961024052610120516102605261014051610280526044356102a052610180516102c0526064356102e05260c061022052610220805160208201209050816103200152602081019050806103005261030090508051602082012090506101a052610120515f610240526101a0516101c052610160516101e052604060a461020037602061024060806101c060015afa5061024051186128e1576044356003610120516020525f5260405f2080610140516020525f5260405f2090505560016101805101600a610120516020525f5260405f205561014051610120517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9256044356101c05260206101c0a360016101c05260206101c0f3611c2d565b63395093518118611c2d576044361034176128e1576004358060a01c6128e1576040526003336020525f5260405f20806040516020525f5260405f209050546024358082018281106128e157905090506060526060516003336020525f5260405f20806040516020525f5260405f20905055604051337f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560605160805260206080a3600160805260206080f3611c2d565b63a457c2d7811861134a576044361034176128e1576004358060a01c6128e1576040526003336020525f5260405f20806040516020525f5260405f209050546024358082038281116128e157905090506060526060516003336020525f5260405f20806040516020525f5260405f20905055604051337f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560605160805260206080a3600160805260206080f35b6390b229978118611c2d576024361034176128e1576004358060011c6128e157604052600b5463f851a440606052602060606004607c845afa61138f573d5f5f3e3d5ffd5b60203d106128e1576060518060a01c6128e15760a05260a090505133186128e157604051600e5500611c2d565b634b8200938118611c2d576024361034176128e1576004358060a01c6128e15761028052336102805181186113f257600161140b565b73d061d61a4d941c39e5453435b6345dc261c2fce08118155b9050156128e15761028051604052611421611cc4565b610280516040526001610280516020525f5260405f20546060526002546080526114496125c3565b60016102a05260206102a0f3611c2d565b63bdf981168118611c2d576024361034176128e1576004358060a01c6128e1576040526040516012336020525f5260405f205500611c2d565b63aace1d488118611c2d576024361034176128e1576004358060a01c6128e15760405233600c5481186114c757600161150a565b600b5463f851a44060c052602060c0600460dc845afa6114e9573d5f5f3e3d5ffd5b60203d106128e15760c0518060a01c6128e157610100526101009050518118155b9050156128e157604051600c557fe83c80a0349150991cb09b4c940149ac4c37cb03673b6c0c669b4da27865c28b60405160605260206060a100611c2d565b6393f7aa67811861156b576044361034176128e15762093a80610300526115a7565b63313ce5678118611c2d57346128e157601260405260206040f3611c2d565b6333b50aed8118611826576064361034176128e157604435610300525b6004358060a01c6128e1576102e0525f546002146128e15760025f5560116102e0516020525f5260405f206001810190505433186128e1575f6040526002546060526040366080376115f76121f9565b6102e0516370a082316103405230610360526020610340602461035c845afa611622573d5f5f3e3d5ffd5b60203d106128e157610340905051610320526102e0516323b872dd61034052336103605230610380526024356103a0526020610340606461035c5f855af161166c573d5f5f3e3d5ffd5b3d61168357803b156128e15760016103c05261169c565b60203d106128e157610340518060011c6128e1576103c0525b6103c0905051156128e1576102e0516370a082316103405230610360526020610340602461035c845afa6116d2573d5f5f3e3d5ffd5b60203d106128e157610340905051610320518082038281116128e157905090506103205260116102e0516020525f5260405f206002810190505461034052610300516103205111156128e157610340514210156117b45761034051428082038281116128e15790509050610360526103605160116102e0516020525f5260405f20600381019050548082028115838383041417156128e157905090506103805261032051610380518082018281106128e157905090506103005180156128e1578082049050905060116102e0516020525f5260405f20600381019050556117e0565b610320516103005180156128e1578082049050905060116102e0516020525f5260405f20600381019050555b4260116102e0516020525f5260405f206004810190505542610300518082018281106128e1579050905060116102e0516020525f5260405f206002810190505560035f55005b63d31f3f6d8118611c2d57346128e157601a546c01431e0fae6d7217ca9fffffff81116128e1576023015460405260206040f3611c2d565b63058a3a248118611c2d576044361034176128e1576004358060a01c6128e1576040526024358060a01c6128e15760605260116040516020525f5260405f20600181019050546080523360805181186118b857600161190f565b600b5463f851a440610100526020610100600461011c845afa6118dd573d5f5f3e3d5ffd5b60203d106128e157610100518060a01c6128e15761014052610140905051811861190857600161190f565b600c548118155b9050156128e157608051156128e157606051156128e15760605160116040516020525f5260405f206001810190505500611c2d565b63e77e74378118611c2d576044361034176128e1576004358060a01c6128e1576040526024358060a01c6128e15760605260146040516020525f5260405f20806060516020525f5260405f209050546fffffffffffffffffffffffffffffffff8116905060805260206080f3611c2d565b6333fd6f748118611c2d576044361034176128e1576004358060a01c6128e1576040526024358060a01c6128e15760605260116060516020525f5260405f206005810190505460805260025460a05260a05115611aca574260116060516020525f5260405f20600281019050548082811882841002189050905060c05260c05160116060516020525f5260405f20600481019050548082038281116128e1579050905060e05260805160e05160116060516020525f5260405f20600381019050548082028115838383041417156128e15790509050670de0b6b3a7640000810281670de0b6b3a76400008204186128e157905060a05180156128e157808204905090508082018281106128e157905090506080525b60136060516020525f5260405f20806040516020525f5260405f2090505460c05260016040516020525f5260405f205460805160c0518082038281116128e157905090508082028115838383041417156128e15790509050670de0b6b3a76400008104905060e05260146040516020525f5260405f20806060516020525f5260405f2090505460801c60e0518082018281106128e15790509050610100526020610100f3611c2d565b63180692d08118611baf57346128e157600f547affffffffffffffffffffffffffffffffffffffffffffffffffffff8116905060405260206040f35b6354fd4d508118611c2d57346128e15760208060805260066040527f76362e312e30000000000000000000000000000000000000000000000000000060605260408160800181518152602082015160208201528051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506080f35b5f5ffd5b60206129855f395f514614611cb9577f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60605260206129656080397f71916ddf3efd1d13d14899519c9cd193fa3f9be07ea852717d2f225de1eb246660a0524660c0523060e05260206129a56101003960c06040526040805160208201209050815250611cc2565b60206129c58239505b565b601a546060526060516c01431e0fae6d7217ca9fffffff81116128e157602301546080526060516c01431e0fae6d7217ca9fffffff81116128e1576c01431e0fae6d7217caa0000023015460a052600f5460c05260c05160d81c60e052600e546101005260c0517affffffffffffffffffffffffffffffffffffffffffffffffffffff811690506101205261012051610140526101005115611d6857604036610120375b60805160e05110611e265763b26b238e610180526020610180600461019c5f73d533a949740bb3306d119cc777fa900ba034cd525af1611daa573d5f5f3e3d5ffd5b60203d106128e157610180516101605261010051611e0957632c4e722e610180526020610180600461019c73d533a949740bb3306d119cc777fa900ba034cd525afa611df8573d5f5f3e3d5ffd5b60203d106128e15761018051610140525b6101605160d81b610140518082018281106128e15790509050600f555b6080514211156120fa576016546101605263615e523761018052306101a052732f50d538606fa9edd2b11e2446beb18c9d5846bb3b156128e1575f610180602461019c5f732f50d538606fa9edd2b11e2446beb18c9d5846bb5af1611e8d573d5f5f3e3d5ffd5b6080516101805260805162093a8081018181106128e157905062093a808104905062093a8081028162093a808204186128e157905042808281188284100218905090506101a0525f6101f4905b806101c0526101a051610180518082038281116128e157905090506101e05263d3078c9461022052306102405261018051610260526020610220604461023c732f50d538606fa9edd2b11e2446beb18c9d5846bb5afa611f3c573d5f5f3e3d5ffd5b60203d106128e157610220516102005261016051156120b0576101805160e0511015611f68575f611f71565b6101a05160e051105b611fd35760a05161012051610200518082028115838383041417156128e157905090506101e0518082028115838383041417156128e157905090506101605180156128e157808204905090508082018281106128e1579050905060a0526120b0565b60a05161012051610200518082028115838383041417156128e1579050905060e051610180518082038281116128e157905090508082028115838383041417156128e157905090506101605180156128e157808204905090508082018281106128e1579050905060a052610140516101205260a05161012051610200518082028115838383041417156128e157905090506101a05160e0518082038281116128e157905090508082028115838383041417156128e157905090506101605180156128e157808204905090508082018281106128e1579050905060a0525b426101a051186120bf576120f7565b6101a051610180526101a05162093a8081018181106128e157905042808281188284100218905090506101a052600101818118611eda575b50505b6060516001810180600f0b81186128e1579050606052606051601a55426060516c01431e0fae6d7217ca9fffffff81116128e1576023015560a0516060516c01431e0fae6d7217ca9fffffff81116128e1576c01431e0fae6d7217caa0000023015560156040516020525f5260405f20546101605260196040516020525f5260405f2080546101605160a05160176040516020525f5260405f20548082038281116128e157905090508082028115838383041417156128e15790509050670de0b6b3a7640000810490508082018281106128e1579050905081555060a05160176040516020525f5260405f20554260186040516020525f5260405f2055565b5f60c05260a05160e052604051156122545760016040516020525f5260405f205460c05260805161222a575f61222f565b60a051155b156122545760126040516020525f5260405f205460e05260e0516122545760405160e0525b601054610100525f6008905b8061012052610100516101205118612277576125bf565b61012051600781116128e157601b0154610140526011610140516020525f5260405f206005810190505461016052426011610140516020525f5260405f20600281019050548082811882841002189050905061018052610180516011610140516020525f5260405f20600481019050548082038281116128e157905090506101a0526101a0511561230c57606051151561230e565b5f5b156123ba57610180516011610140516020525f5260405f2060048101905055610160516101a0516011610140516020525f5260405f20600381019050548082028115838383041417156128e15790509050670de0b6b3a7640000810281670de0b6b3a76400008204186128e157905060605180156128e157808204905090508082018281106128e1579050905061016052610160516011610140516020525f5260405f20600581019050555b604051156125b4576013610140516020525f5260405f20806040516020525f5260405f209050546101c0525f6101e052610160516101c051101561245a57610160516013610140516020525f5260405f20806040516020525f5260405f2090505560c051610160516101c0518082038281116128e157905090508082028115838383041417156128e15790509050670de0b6b3a7640000810490506101e0525b60146040516020525f5260405f2080610140516020525f5260405f20905054610200526102005160801c6101e0518082018281106128e157905090506102205261022051156125b457610200516fffffffffffffffffffffffffffffffff811690506102405260805161250d576101e051156125b457610240516102205160801b8082018281106128e1579050905060146040516020525f5260405f2080610140516020525f5260405f209050556125b4565b6101405163a9059cbb6102605260e05161028052610220516102a0526020610260604461027c5f855af1612543573d5f5f3e3d5ffd5b3d61255a57803b156128e15760016102c052612573565b60203d106128e157610260518060011c6128e1576102c0525b6102c0905051156128e15761024051610220518082018281106128e1579050905060146040516020525f5260405f2080610140516020525f5260405f209050555b600101818118612260575b5050565b63bbf7408a60c05260405160e052602060c0602460dc738e0c00ed546602fd9927df742bbabf726d5b0d165afa6125fc573d5f5f3e3d5ffd5b60203d106128e15760c05160a0526318160ddd60e052602060e0600460fc735f3b5dfeb7b28cdbd7faba78963ee202a494e2a25afa61263d573d5f5f3e3d5ffd5b60203d106128e15760e05160c052606051602881028160288204186128e157905060648104905060e05260c051156126c45760e05160805160a0518082028115838383041417156128e1579050905060c05180156128e15780820490509050603c810281603c8204186128e15790506064810490508082018281106128e1579050905060e0525b60605160e0518082811882841002189050905060e05260156040516020525f5260405f20546101005260e05160156040516020525f5260405f205560165460e0518082018281106128e15790509050610100518082038281116128e1579050905061012052610120516016556040517f7ecd84343f76a23d2227290e0288da3251b045541698e575a5515af4f04197a3606051610140526080516101605260e05161018052610120516101a0526080610140a2565b6102e051604052612788611cc4565b61030051604052612797611cc4565b61032051156128a8576002546103405260105415156103605261036051156127d5576102e051604052610340516060526040366080376127d56121f9565b60016102e0516020525f5260405f2054610320518082038281116128e15790509050610380526103805160016102e0516020525f5260405f20556102e051604052610380516060526103405160805261282c6125c3565b61036051156128515761030051604052610340516060526040366080376128516121f9565b6001610300516020525f5260405f2054610320518082018281106128e1579050905061038052610380516001610300516020525f5260405f20556103005160405261038051606052610340516080526128a86125c3565b610300516102e0517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef61032051610340526020610340a3565b5f80fd047203f31c2d02c91c2d1c2d1c2d08ec1493082f07f01c2d0b6c0c441c2d0fe81b7311ec1c2d13bc1c2d04d3145a129d07941c2d1c2d0f061c2d0131072a1c2d1c2d1c2d1c2d1c2d185e0bee06ea15491c2d0b1c1c2d1c2d1c2d158a1c2d1c2d1c2d00b7088e001819b505150d6319441c2d070a084f0f861c2d0afc023f1c2dc8ee04fdc2a10ca266f610dee88f8c2e5e8e24119b7a8cb3b875c96e752eafad000000000000000000000000000000000000000000000000000000000000000160cd0881331f0a20bd8e377e8a9dea3ccfcc801d79964789cacc91d949c21fc34e00744274f25bf8def6ad677384cfdd6e3a93ee052683c404982ed6c320866d