pragma solidity >=0.8.0; // SPDX-License-Identifier: AGPL-3.0-or-later // File-Version: 1 contract CappedToken { // Implements EIP173 address public owner; // Implements Writer mapping(address => bool) writer; // Implements ERC20 string public name; // Implements ERC20 string public symbol; // Implements ERC20 uint8 public decimals; // Implements ERC20 mapping (address => uint256) public balanceOf; // Implements ERC20 mapping (address => mapping (address => uint256)) public allowance; // Implements Burner uint256 public totalMinted; // Implements Burner uint256 public totalBurned; // Implements Expire uint256 public expires; bool expired; // Implements Cap uint256 public maxSupply; mapping(address => bool) writers; // Implements ERC20 event Transfer(address indexed _from, address indexed _to, uint256 _value); // Implements ERC20 event TransferFrom(address indexed _from, address indexed _to, address indexed _spender, uint256 _value); // Implements ERC20 event Approval(address indexed _owner, address indexed _spender, uint256 _value); // Implements Minter event Mint(address indexed _minter, address indexed _beneficiary, uint256 _value); // Implement Expire event Expired(uint256 _timestamp); event ExpiryChange(uint256 indexed _oldTimestamp, uint256 _newTimestamp); // Implements Writer event WriterAdded(address _writer); // Implements Writer event WriterRemoved(address _writer); // Implements Burner event Burn(uint256 _value); // Implements Cap event Cap(uint256 indexed _oldCap, uint256 _newCap); // Implements Seal event SealStateChange(bool indexed _final, uint256 _sealState); // Implements Seal uint256 public sealState; uint8 constant WRITER_STATE = 1; uint8 constant CAP_STATE = 2; uint8 constant EXPIRY_STATE = 4; uint256 constant public maxSealState = 15; constructor(string memory _name, string memory _symbol, uint8 _decimals) { owner = msg.sender; name = _name; symbol = _symbol; decimals = _decimals; } // Change max token supply. // Can only increase supply cap, not decrease. function setMaxSupply(uint256 _cap) public { require(!isSealed(CAP_STATE)); require(msg.sender == owner); require(_cap > totalSupply()); emit Cap(maxSupply, _cap); maxSupply = _cap; } function seal(uint256 _state) public returns(uint256) { require(_state < maxSealState + 1, 'ERR_INVALID_STATE'); require(_state & sealState == 0, 'ERR_ALREADY_LOCKED'); sealState |= _state; emit SealStateChange(sealState == maxSealState, sealState); return uint256(sealState); } function isSealed(uint256 _state) public view returns(bool) { require(_state < maxSealState); if (_state == 0) { return sealState == maxSealState; } return _state & sealState == _state; } function setExpire(uint256 _timestamp) public { require(!isSealed(EXPIRY_STATE), "ERR_SEALED"); require(_timestamp < block.timestamp, "ERR_EXPIRE_PAST"); uint256 oldTimestamp; oldTimestamp = expires; expires = _timestamp; emit ExpiryChange(oldTimestamp, expires); } // Implements ERC20 function totalSupply() public view returns (uint256) { return totalMinted - totalBurned; } // Implements Minter function mintTo(address _to, uint256 _value) public returns (bool) { require(writers[msg.sender] || msg.sender == owner, "ERR_AXX"); require(applyExpiry() == 0, "ERR_EXPIRE"); if (maxSupply > 0) { require(totalSupply() + _value <= maxSupply); } balanceOf[_to] += _value; totalMinted += _value; emit Mint(msg.sender, _to, _value); return true; } // Implements Minter // Implements ERC5679Ext20 function mint(address _to, uint256 _value, bytes calldata _data) public { _data; mintTo(_to, _value); } // Implements Writer function addWriter(address _minter) public returns (bool) { require(!isSealed(WRITER_STATE), "ERR_SEALED"); require(msg.sender == owner); writers[_minter] = true; return true; } // Implements Writer function deleteWriter(address _minter) public returns (bool) { require(!isSealed(WRITER_STATE), "ERR_SEALED"); require(msg.sender == owner || msg.sender == _minter); writers[_minter] = false; return true; } // Implements Writer function isWriter(address _minter) public view returns(bool) { return writers[_minter] || _minter == owner; } // Implements Expire function applyExpiry() public returns(uint8) { if (expires == 0) { return 0; } if (expired) { return 1; } if (block.timestamp >= expires) { expired = true; emit Expired(block.timestamp); return 2; } return 0; } // Implements ERC20 function transfer(address _to, uint256 _value) public returns (bool) { require(applyExpiry() == 0); require(balanceOf[msg.sender] >= _value); balanceOf[msg.sender] -= _value; balanceOf[_to] += _value; emit Transfer(msg.sender, _to, _value); return true; } // Implements Burner function burn(uint256 _value) public returns (bool) { require(msg.sender == owner, 'ERR_ACCESS'); require(balanceOf[msg.sender] >= _value, 'ERR_FUNDS'); balanceOf[msg.sender] -= _value; totalBurned += _value; emit Burn(_value); return true; } // Implements Burner function burn() public returns(bool) { return burn(balanceOf[msg.sender]); } // Implements Burner // Implements ERC5679Ext20 function burn(address _from, uint256 _value, bytes calldata _data) public { require(msg.sender == _from, 'ERR_NOT_SELF'); _data; burn(_value); } // Implements ERC20 function transferFrom(address _from, address _to, uint256 _value) public returns (bool) { require(applyExpiry() == 0); require(allowance[_from][msg.sender] >= _value); require(balanceOf[_from] >= _value); allowance[_from][msg.sender] = allowance[_from][msg.sender] - _value; balanceOf[_from] -= _value; balanceOf[_to] += _value; emit TransferFrom(_from, _to, msg.sender, _value); return true; } // Implements ERC20 function approve(address _spender, uint256 _value) public returns (bool) { require(applyExpiry() == 0); if (_value > 0) { require(allowance[msg.sender][_spender] == 0); } allowance[msg.sender][_spender] = _value; emit Approval(msg.sender, _spender, _value); return true; } // Implements EIP173 function transferOwnership(address _newOwner) public returns (bool) { require(msg.sender == owner); owner = _newOwner; return true; } // Implements EIP165 function supportsInterface(bytes4 _sum) public pure returns (bool) { if (_sum == 0xb61bc941) { // ERC20 return true; } if (_sum == 0x449a52f8) { // Minter return true; } if (_sum == 0x01ffc9a7) { // EIP165 return true; } if (_sum == 0x9493f8b2) { // EIP173 return true; } if (_sum == 0xabe1f1f5) { // Writer return true; } if (_sum == 0xb1110c1b) { // Burner return true; } if (_sum == 0x841a0e94) { // Expire return true; } return false; } }