# @version 0.2.12
# @author banteg
# @license MIT
from vyper.interfaces import ERC20
implements: ERC20
event Transfer:
sender: indexed(address)
receiver: indexed(address)
value: uint256
event Approval:
owner: indexed(address)
spender: indexed(address)
value: uint256
event AdminChanged:
new_admin: address
event MinterChanged:
new_minter: address
name: public(String[26])
symbol: public(String[7])
decimals: public(uint256)
version: public(String[1])
balanceOf: public(HashMap[address, uint256])
allowance: public(HashMap[address, HashMap[address, uint256]])
totalSupply: public(uint256)
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)')
admin: public(address)
minter: public(address)
@external
def __init__(_symbol: String[7], _minter: address, _admin: address):
self.name = 'bETH'
self.symbol = _symbol
self.decimals = 18
self.version = '1'
self.DOMAIN_SEPARATOR = keccak256(
concat(
DOMAIN_TYPE_HASH,
keccak256(convert(self.name, Bytes[26])),
keccak256(convert(self.version, Bytes[1])),
convert(chain.id, bytes32),
convert(self, bytes32)
)
)
self.minter = _minter
self.admin = _admin
log AdminChanged(_admin)
log MinterChanged(_minter)
@external
def change_admin(new_admin: address):
assert msg.sender == self.admin
self.admin = new_admin
log AdminChanged(new_admin)
@external
def set_minter(new_minter: address):
assert msg.sender == self.admin
self.minter = new_minter
log MinterChanged(new_minter)
@external
def mint(owner: address, amount: uint256):
assert msg.sender == self.minter
self.totalSupply += amount
self.balanceOf[owner] += amount
log Transfer(ZERO_ADDRESS, owner, amount)
@external
def burn(owner: address, amount: uint256):
assert msg.sender == self.minter
self.totalSupply -= amount
self.balanceOf[owner] -= amount
log Transfer(owner, ZERO_ADDRESS, amount)
@internal
def _transfer(sender: address, receiver: address, amount: uint256):
assert receiver not in [self, ZERO_ADDRESS]
self.balanceOf[sender] -= amount
self.balanceOf[receiver] += amount
log Transfer(sender, receiver, amount)
@external
def transfer(receiver: address, amount: uint256) -> bool:
self._transfer(msg.sender, receiver, amount)
return True
@external
def transferFrom(sender: address, receiver: address, amount: uint256) -> bool:
if msg.sender != sender and self.allowance[sender][msg.sender] != MAX_UINT256:
self.allowance[sender][msg.sender] -= amount
log Approval(sender, msg.sender, self.allowance[sender][msg.sender])
self._transfer(sender, receiver, 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 permit(owner: address, spender: address, amount: uint256, expiry: uint256, signature: Bytes[65]) -> bool:
assert owner != ZERO_ADDRESS # dev: invalid owner
assert expiry == 0 or expiry >= block.timestamp # dev: permit expired
nonce: uint256 = self.nonces[owner]
digest: bytes32 = keccak256(
concat(
b'\x19\x01',
self.DOMAIN_SEPARATOR,
keccak256(
concat(
PERMIT_TYPE_HASH,
convert(owner, bytes32),
convert(spender, bytes32),
convert(amount, bytes32),
convert(nonce, bytes32),
convert(expiry, bytes32),
)
)
)
)
# NOTE: the signature is packed as r, s, v
r: uint256 = convert(slice(signature, 0, 32), uint256)
s: uint256 = convert(slice(signature, 32, 32), uint256)
v: uint256 = convert(slice(signature, 64, 1), uint256)
assert ecrecover(digest, v, r, s) == owner # dev: invalid signature
self.allowance[owner][spender] = amount
self.nonces[owner] = nonce + 1
log Approval(owner, spender, amount)
return True
{
"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": [
"."
]
}
[{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"},{"indexed":true,"name":"receiver","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"new_admin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"new_minter","type":"address"}],"name":"MinterChanged","type":"event"},{"inputs":[{"name":"_symbol","type":"string"},{"name":"_minter","type":"address"},{"name":"_admin","type":"address"}],"outputs":[],"stateMutability":"nonpayable","type":"constructor"},{"gas":38895,"inputs":[{"name":"new_admin","type":"address"}],"name":"change_admin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"gas":38925,"inputs":[{"name":"new_minter","type":"address"}],"name":"set_minter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"gas":79414,"inputs":[{"name":"owner","type":"address"},{"name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"gas":79432,"inputs":[{"name":"owner","type":"address"},{"name":"amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"gas":78648,"inputs":[{"name":"receiver","type":"address"},{"name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"gas":123130,"inputs":[{"name":"sender","type":"address"},{"name":"receiver","type":"address"},{"name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"gas":37911,"inputs":[{"name":"spender","type":"address"},{"name":"amount","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"gas":91104,"inputs":[{"name":"owner","type":"address"},{"name":"spender","type":"address"},{"name":"amount","type":"uint256"},{"name":"expiry","type":"uint256"},{"name":"signature","type":"bytes"}],"name":"permit","outputs":[{"name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"gas":10653,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"stateMutability":"view","type":"function"},{"gas":10683,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"stateMutability":"view","type":"function"},{"gas":2688,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"gas":10743,"inputs":[],"name":"version","outputs":[{"name":"","type":"string"}],"stateMutability":"view","type":"function"},{"gas":2963,"inputs":[{"name":"arg0","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"gas":3208,"inputs":[{"name":"arg0","type":"address"},{"name":"arg1","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"gas":2808,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"gas":3053,"inputs":[{"name":"arg0","type":"address"}],"name":"nonces","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"gas":2868,"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"gas":2898,"inputs":[],"name":"admin","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"},{"gas":2928,"inputs":[],"name":"minter","outputs":[{"name":"","type":"address"}],"stateMutability":"view","type":"function"}]