账户
0x35...6018
0x35...6018

0x35...6018

US$0.00
此合同的源代码已经过验证!
合同元数据
编译器
0.3.1
语言
Vyper
合同源代码
文件 1 的 1:Vyper_contract.vy
# @version 0.3.1
"""
@title Token Minter
@author Curve Finance
@license MIT
"""

interface LiquidityGauge:
    # Presumably, other gauges will provide the same interfaces
    def integrate_fraction(addr: address) -> uint256: view
    def user_checkpoint(addr: address) -> bool: nonpayable

interface ERC20:
    def transfer(to: address, amount: uint256) -> bool: nonpayable
    def balanceOf(account: address) -> uint256: nonpayable

interface GaugeController:
    def gauge_types(addr: address) -> int128: view

event Minted:
    recipient: indexed(address)
    gauge: address
    minted: uint256

event UpdateMiningParameters:
    time: uint256
    rate: uint256

event CommitNextEmission:
    rate: uint256

event CommitEmergencyReturn:
    admin: address

event ApplyEmergencyReturn:
    admin: address

event CommitOwnership:
    admin: address

event ApplyOwnership:
    admin: address

# General constants
WEEK: constant(uint256) = 86400 * 7

# 1.25M SDL / WEEK
INITIAL_RATE: constant(uint256) = 1_250_000 * 10 ** 18 / WEEK
# Weekly
MAX_ABS_RATE: constant(uint256) = 10_000_000 * 10 ** 18
RATE_REDUCTION_TIME: constant(uint256) = WEEK * 2

mining_epoch: public(int128)
start_epoch_time: public(uint256)
rate: public(uint256)
committed_rate: public(uint256)
is_start: public(bool)

token: public(address)
controller: public(address)

# user -> gauge -> value
minted: public(HashMap[address, HashMap[address, uint256]])

# minter -> user -> can mint?
allowed_to_mint_for: public(HashMap[address, HashMap[address, bool]])

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

@external
def __init__(_token: address, _controller: address, _emergency_return: address, _admin: address):
    self.token = _token
    self.controller = _controller
    self.emergency_return = _emergency_return
    self.admin = _admin

    self.start_epoch_time = block.timestamp - RATE_REDUCTION_TIME
    self.mining_epoch = -1
    self.is_start = True
    self.committed_rate = MAX_UINT256


@internal
def _update_mining_parameters():
    """
    @dev Update mining rate and supply at the start of the epoch
         Any modifying mining call must also call this
    """
    _rate: uint256 = self.rate

    self.start_epoch_time += RATE_REDUCTION_TIME
    self.mining_epoch += 1

    if _rate == 0 and self.is_start:
        _rate = INITIAL_RATE
        self.is_start = False
    else:
        _committed_rate: uint256 = self.committed_rate
        if _committed_rate != MAX_UINT256:
          _rate = _committed_rate
          self.committed_rate = MAX_UINT256

    self.rate = _rate

    log UpdateMiningParameters(block.timestamp, _rate)

@external
def update_mining_parameters():
    """
    @notice Update mining rate and supply at the start of the epoch
    @dev Callable by any address, but only once per epoch
         Total supply becomes slightly larger if this function is called late
    """
    assert block.timestamp >= self.start_epoch_time + RATE_REDUCTION_TIME  # dev: too soon!
    self._update_mining_parameters()

@external
def start_epoch_time_write() -> uint256:
    """
    @notice Get timestamp of the current mining epoch start
            while simultaneously updating mining parameters
    @return Timestamp of the epoch
    """
    _start_epoch_time: uint256 = self.start_epoch_time
    if block.timestamp >= _start_epoch_time + RATE_REDUCTION_TIME:
        self._update_mining_parameters()
        return self.start_epoch_time
    else:
        return _start_epoch_time

@external
def future_epoch_time_write() -> uint256:
    """
    @notice Get timestamp of the next mining epoch start
            while simultaneously updating mining parameters
    @return Timestamp of the next epoch
    """
    _start_epoch_time: uint256 = self.start_epoch_time
    if block.timestamp >= _start_epoch_time + RATE_REDUCTION_TIME:
        self._update_mining_parameters()
        return self.start_epoch_time + RATE_REDUCTION_TIME
    else:
        return _start_epoch_time + RATE_REDUCTION_TIME

@internal
def _mint_for(gauge_addr: address, _for: address):
    assert GaugeController(self.controller).gauge_types(gauge_addr) >= 0  # dev: gauge is not added

    LiquidityGauge(gauge_addr).user_checkpoint(_for)
    total_mint: uint256 = LiquidityGauge(gauge_addr).integrate_fraction(_for)
    to_mint: uint256 = total_mint - self.minted[_for][gauge_addr]

    if to_mint != 0:
        ERC20(self.token).transfer(_for, to_mint)
        if block.timestamp >= self.start_epoch_time + RATE_REDUCTION_TIME:
          self._update_mining_parameters()
        self.minted[_for][gauge_addr] = total_mint

        log Minted(_for, gauge_addr, total_mint)


@external
@nonreentrant('lock')
def mint(gauge_addr: address):
    """
    @notice Mint everything which belongs to `msg.sender` and send to them
    @param gauge_addr `LiquidityGauge` address to get mintable amount from
    """
    self._mint_for(gauge_addr, msg.sender)


@external
@nonreentrant('lock')
def mint_many(gauge_addrs: address[8]):
    """
    @notice Mint everything which belongs to `msg.sender` across multiple gauges
    @param gauge_addrs List of `LiquidityGauge` addresses
    """
    for i in range(8):
        if gauge_addrs[i] == ZERO_ADDRESS:
            break
        self._mint_for(gauge_addrs[i], msg.sender)


@external
@nonreentrant('lock')
def mint_for(gauge_addr: address, _for: address):
    """
    @notice Mint tokens for `_for`
    @dev Only possible when `msg.sender` has been approved via `toggle_approve_mint`
    @param gauge_addr `LiquidityGauge` address to get mintable amount from
    @param _for Address to mint to
    """
    if self.allowed_to_mint_for[msg.sender][_for]:
        self._mint_for(gauge_addr, _for)


@external
def toggle_approve_mint(minting_user: address):
    """
    @notice allow `minting_user` to mint for `msg.sender`
    @param minting_user Address to toggle permission for
    """
    self.allowed_to_mint_for[minting_user][msg.sender] = not self.allowed_to_mint_for[minting_user][msg.sender]

@external
def recover_balance(_coin: address) -> bool:
    """
    @notice Recover ERC20 tokens from this contract
    @dev Tokens are sent to the emergency return address.
    @param _coin Token address
    @return bool success
    """
    assert msg.sender == self.admin # dev: admin only

    amount: uint256 = ERC20(_coin).balanceOf(self)
    response: Bytes[32] = raw_call(
        _coin,
        concat(
            method_id("transfer(address,uint256)"),
            convert(self.emergency_return, bytes32),
            convert(amount, bytes32),
        ),
        max_outsize=32,
    )
    if len(response) != 0:
        assert convert(response, bool)

    return True

@external
def commit_next_emission(_rate_per_week: uint256):
  """
  @notice Commit a new rate for the following week (we update by weeks).
          _rate_per_week should have no decimals (ex: if we want to reward 600_000 SDL over the course of a week, we pass in 600_000 * 10 ** 18)
  """
  assert msg.sender == self.admin # dev: admin only
  assert _rate_per_week <= MAX_ABS_RATE # dev: preventing fatfinger
  new_rate: uint256 = _rate_per_week / WEEK
  self.committed_rate = new_rate
  log CommitNextEmission(new_rate)

@external
def commit_transfer_emergency_return(addr: address):
    """
    @notice Update emergency ret. of Minter to `addr`
    @param addr Address to have emergency ret. transferred to
    """
    assert msg.sender == self.admin  # dev: admin only
    self.future_emergency_return = addr
    log CommitEmergencyReturn(addr)

@external
def apply_transfer_emergency_return():
    """
    @notice Apply pending emergency ret. update
    """
    assert msg.sender == self.admin  # dev: admin only
    _emergency_return: address = self.future_emergency_return
    assert _emergency_return != ZERO_ADDRESS  # dev: emergency return not set
    self.emergency_return = _emergency_return
    log ApplyEmergencyReturn(_emergency_return)

@external
def commit_transfer_ownership(addr: address):
    """
    @notice Transfer ownership of GaugeController to `addr`
    @param addr Address to have ownership transferred to
    """
    assert msg.sender == self.admin  # dev: admin only
    self.future_admin = addr
    log CommitOwnership(addr)

@external
def apply_transfer_ownership():
    """
    @notice Apply pending ownership transfer
    """
    assert msg.sender == self.admin  # dev: admin only
    _admin: address = self.future_admin
    assert _admin != ZERO_ADDRESS  # dev: admin not set
    self.admin = _admin
    log ApplyOwnership(_admin)
设置
{
  "compilationTarget": {
    "Vyper_contract.vy": "Vyper_contract"
  },
  "outputSelection": {
    "Vyper_contract.vy": [
      "abi",
      "ast",
      "interface",
      "ir",
      "userdoc",
      "devdoc",
      "evm.bytecode.object",
      "evm.bytecode.opcodes",
      "evm.deployedBytecode.object",
      "evm.deployedBytecode.opcodes",
      "evm.deployedBytecode.sourceMap",
      "evm.methodIdentifiers"
    ]
  },
  "search_paths": [
    "."
  ]
}
ABI
[{"anonymous":false,"inputs":[{"indexed":true,"name":"recipient","type":"address"},{"indexed":false,"name":"gauge","type":"address"},{"indexed":false,"name":"minted","type":"uint256"}],"name":"Minted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"time","type":"uint256"},{"indexed":false,"name":"rate","type":"uint256"}],"name":"UpdateMiningParameters","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"rate","type":"uint256"}],"name":"CommitNextEmission","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"admin","type":"address"}],"name":"CommitEmergencyReturn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"admin","type":"address"}],"name":"ApplyEmergencyReturn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"admin","type":"address"}],"name":"CommitOwnership","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"admin","type":"address"}],"name":"ApplyOwnership","type":"event"},{"inputs":[{"name":"_token","type":"address"},{"name":"_controller","type":"address"},{"name":"_emergency_return","type":"address"},{"name":"_admin","type":"address"}],"outputs":[],"stateMutability":"nonpayable","type":"constructor"},{"gas":155943,"inputs":[],"name":"update_mining_parameters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"gas":158152,"inputs":[],"name":"start_epoch_time_write","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"gas":158341,"inputs":[],"name":"future_epoch_time_write","outputs":[{"name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"gas":268280,"inputs":[{"name":"gauge_addr","type":"address"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"gas":1745103,"inputs":[{"name":"gauge_addrs","type":"address[8]"}],"name":"mint_many","outputs":[],"stateMutability":"nonpayable","type":"function"},{"gas":270873,"inputs":[{"name":"gauge_addr","type":"address"},{"name":"_for","type":"address"}],"name":"mint_for","outputs":[],"stateMutability":"nonpayable","type":"function"},{"gas":38141,"inputs":[{"name":"minting_user","type":"address"}],"name":"toggle_approve_mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"gas":14650,"inputs":[{"name":"_coin","type":"address"}],"name":"recover_balance","outputs":[{"name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"gas":39683,"inputs":[{"name":"_rate_per_week","type":"uint256"}],"name":"commit_next_emission","outputs":[],"stateMutability":"nonpayable","type":"function"},{"gas":39715,"inputs":[{"name":"addr","type":"address"}],"name":"commit_transfer_emergency_return","outputs":[],"stateMutability":"nonpayable","type":"function"},{"gas":41806,"inputs":[],"name":"apply_transfer_emergency_return","outputs":[],"stateMutability":"nonpayable","type":"function"},{"gas":39775,"inputs":[{"name":"addr","type":"address"}],"name":"commit_transfer_ownership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"gas":41866,"inputs":[],"name":"apply_transfer_ownership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"gas":2850,"inputs":[],"name":"mining_epoch","outputs":[{"name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"gas":2880,"inputs":[],"name":"start_epoch_time","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"gas":2910,"inputs":[],"name":"rate","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"gas":2940,"inputs":[],"name":"committed_rate","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"gas":2970,"inputs":[],"name":"is_start","outputs":[{"name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"gas":3000,"inputs":[],"name":"token","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"gas":3030,"inputs":[],"name":"controller","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"gas":3592,"inputs":[{"name":"arg0","type":"address"},{"name":"arg1","type":"address"}],"name":"minted","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"gas":3622,"inputs":[{"name":"arg0","type":"address"},{"name":"arg1","type":"address"}],"name":"allowed_to_mint_for","outputs":[{"name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"gas":3120,"inputs":[],"name":"future_emergency_return","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"gas":3150,"inputs":[],"name":"emergency_return","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"gas":3180,"inputs":[],"name":"admin","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"gas":3210,"inputs":[],"name":"future_admin","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"}]