ETH Price: $3,612.72 (+0.95%)

Contract Diff Checker

Contract Name:
Vyper_contract

Contract Source Code:

File 1 of 1 : Vyper_contract

# @version 0.2.12

"""
@title Unagii Token
@author stakewith.us
@license AGPL-3.0-or-later
"""

VERSION: constant(String[28]) = "0.1.1"

from vyper.interfaces import ERC20

implements: ERC20


interface DetailedERC20:
    def name() -> String[42]: view
    def symbol() -> String[20]: view
    # Vyper does not support uint8
    def decimals() -> uint256: view


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


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


event SetNextTimeLock:
    timeLock: address


event AcceptTimeLock:
    timeLock: address


event SetMinter:
    minter: address


name: public(String[64])
symbol: public(String[32])
# Vyper does not support uint8
decimals: public(uint256)
balanceOf: public(HashMap[address, uint256])
allowance: public(HashMap[address, HashMap[address, uint256]])
totalSupply: public(uint256)

# EIP 2612 #
# https://eips.ethereum.org/EIPS/eip-2612
# `nonces` track `permit` approvals with signature.
nonces: public(HashMap[address, uint256])
DOMAIN_SEPARATOR: public(bytes32)
DOMAIN_TYPE_HASH: constant(bytes32) = keccak256(
    "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
)
PERMIT_TYPE_HASH: constant(bytes32) = keccak256(
    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
)

timeLock: public(address)
nextTimeLock: public(address)
minter: public(address)
token: public(ERC20)
# placeholder address used when token ETH
ETH: constant(address) = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE
# last block number balance of msg.sender was changed (mint, burn, transfer, transferFrom)
lastBlock: public(HashMap[address, uint256])


@external
def __init__(token: address):
    self.timeLock = msg.sender
    self.token = ERC20(token)

    if token == ETH:
        self.name = "unagii_ETH_v2"
        self.symbol = "uETHv2"
        self.decimals = 18
    else:
        self.name = concat("unagii_", DetailedERC20(token).name(), "_v2")
        self.symbol = concat("u", DetailedERC20(token).symbol(), "v2")
        self.decimals = DetailedERC20(token).decimals()

    self.DOMAIN_SEPARATOR = keccak256(
        concat(
            DOMAIN_TYPE_HASH,
            keccak256(convert("unagii", Bytes[6])),
            keccak256(convert(VERSION, Bytes[28])),
            convert(chain.id, bytes32),
            convert(self, bytes32),
        )
    )


@internal
@view
def _getDomainSeparator() -> bytes32:
    return keccak256(
        concat(
            DOMAIN_TYPE_HASH,
            keccak256(convert("unagii", Bytes[6])),
            keccak256(convert(VERSION, Bytes[28])),
            convert(chain.id, bytes32),
            convert(self, bytes32),
        )
    )


@external
def setName(name: String[42]):
    assert msg.sender == self.timeLock, "!time lock"
    self.name = name


@external
def setSymbol(symbol: String[20]):
    assert msg.sender == self.timeLock, "!time lock"
    self.symbol = symbol


@external
def setNextTimeLock(nextTimeLock: address):
    """
    @notice Set next time lock
    @param nextTimeLock Address of next time lock
    """
    assert msg.sender == self.timeLock, "!time lock"
    # allow next time lock = zero address (cancel next time lock)
    self.nextTimeLock = nextTimeLock
    log SetNextTimeLock(nextTimeLock)


@external
def acceptTimeLock():
    """
    @notice Accept time lock
    @dev Only `nextTimeLock` can claim time lock
    """
    assert msg.sender == self.nextTimeLock, "!next time lock"
    self.timeLock = msg.sender
    self.nextTimeLock = ZERO_ADDRESS
    log AcceptTimeLock(msg.sender)


@external
def setMinter(minter: address):
    """
    @notice Set minter
    @param minter Address of minter
    """
    assert msg.sender == self.timeLock, "!time lock"
    # allow minter = zero address
    self.minter = minter
    log SetMinter(minter)


@internal
def _transfer(_from: address, _to: address, amount: uint256):
    assert _to not in [self, ZERO_ADDRESS], "invalid receiver"

    # track lastest tx
    self.lastBlock[_from] = block.number
    self.lastBlock[_to] = block.number

    self.balanceOf[_from] -= amount
    self.balanceOf[_to] += amount
    log Transfer(_from, _to, amount)


@external
def transfer(_to: address, amount: uint256) -> bool:
    self._transfer(msg.sender, _to, amount)
    return True


@external
def transferFrom(_from: address, _to: address, amount: uint256) -> bool:
    # skip if unlimited approval
    if self.allowance[_from][msg.sender] < MAX_UINT256:
        self.allowance[_from][msg.sender] -= amount
        log Approval(_from, msg.sender, self.allowance[_from][msg.sender])
    self._transfer(_from, _to, amount)
    return True


@external
def approve(spender: address, amount: uint256) -> bool:
    self.allowance[msg.sender][spender] = amount
    log Approval(msg.sender, spender, amount)
    return True


@external
def increaseAllowance(spender: address, amount: uint256) -> bool:
    self.allowance[msg.sender][spender] += amount
    log Approval(msg.sender, spender, self.allowance[msg.sender][spender])
    return True


@external
def decreaseAllowance(spender: address, amount: uint256) -> bool:
    self.allowance[msg.sender][spender] -= amount
    log Approval(msg.sender, spender, self.allowance[msg.sender][spender])
    return True


@internal
@view
def _recover(digest: bytes32, v: uint256, r: bytes32, s: bytes32) -> address: 
    """
    @dev ECDSA signature malleability.
         Code ported from Solidity
         https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/cryptography/ECDSA.sol#L53
    """
    _r: uint256 = convert(r, uint256)
    _s: uint256 = convert(s, uint256)

    # 0x7FF... is intentionally not stored as constant(uint256) so that code is
    # compared with OpenZeppelin's Solidity code
    assert _s <= convert(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, uint256), "invalid signature s"
    assert v == 27 or v == 28, "invalid signature v"

    return ecrecover(digest, v, _r, _s)


@external
def permit(
    owner: address,
    spender: address,
    amount: uint256,
    deadline: uint256,
    v: uint256,
    r: bytes32,
    s: bytes32,
):
    """
    @notice Approves spender by owner's signature to expend owner's tokens.
            https://eips.ethereum.org/EIPS/eip-2612
    @dev Vyper does not have `uint8`, so replace `v: uint8` with `v: uint256`
    """
    assert owner != ZERO_ADDRESS, "owner = 0 address"
    assert deadline >= block.timestamp, "expired"

    digest: bytes32 = keccak256(
        concat(
            b"\x19\x01",
            self._getDomainSeparator(), # chain id may be different after fork, recompute domain separator
            keccak256(
                concat(
                    PERMIT_TYPE_HASH,
                    convert(owner, bytes32),
                    convert(spender, bytes32),
                    convert(amount, bytes32),
                    convert(self.nonces[owner], bytes32),
                    convert(deadline, bytes32),
                )
            ),
        )
    )

    # owner cannot = ZERO_ADDRESS from check above
    # this will fail if _recover() returns ZERO_ADDRESS
    assert self._recover(digest, v, r, s) == owner, "invalid signature"

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


@external
def mint(_to: address, amount: uint256):
    assert msg.sender == self.minter, "!minter"
    assert _to not in [self, ZERO_ADDRESS], "invalid receiver"

    # track lastest tx
    self.lastBlock[_to] = block.number

    self.totalSupply += amount
    self.balanceOf[_to] += amount
    log Transfer(ZERO_ADDRESS, _to, amount)


@external
def burn(_from: address, amount: uint256):
    assert msg.sender == self.minter, "!minter"
    assert _from != ZERO_ADDRESS, "from = 0"

    # track lastest tx
    self.lastBlock[_from] = block.number

    self.totalSupply -= amount
    self.balanceOf[_from] -= amount
    log Transfer(_from, ZERO_ADDRESS, amount)

Please enter a contract address above to load the contract details and source code.

Context size (optional):