// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
import {IERC20} from "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.1.0/contracts/token/ERC20/ERC20.sol";
import {SafeMath} from "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.1.0/contracts/utils/math/SafeMath.sol";
import {Ownable} from "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.1.0/contracts/access/Ownable.sol";
/**
* @title Unizen Real Circulating Supply
* @dev Simple contract to easily retrieve the current
* market of Unizen utility token ZCX
*
*/
contract UnizenRealCirculatingSupply is Ownable {
struct AddressInfo {
address lockAddress;
string name;
}
IERC20 public zcxToken;
mapping(uint8 => AddressInfo) public lockedAddresses;
mapping(uint8 => address) public burnAddresses;
uint8 public lockedAddressCount;
uint8 public burnAddressCount;
uint8 internal _lockIdx;
uint8 internal _burnIdx;
constructor(address _tokenAddress) {
require(_tokenAddress != address(0), "ZERO_ADDRESS");
zcxToken = IERC20(_tokenAddress);
}
/* === VIEW FUNCTIONS === */
function getCirculatingSupply() external view returns(uint256 circulatingSupply) {
uint256 totalSupply = zcxToken.totalSupply();
circulatingSupply = totalSupply;
for(uint8 i = 0; i < _lockIdx; i++) {
if(lockedAddresses[i].lockAddress != address(0)) {
circulatingSupply = SafeMath.sub(circulatingSupply, zcxToken.balanceOf(lockedAddresses[i].lockAddress));
}
}
for(uint8 i = 0; i < _burnIdx; i++) {
if(burnAddresses[i] != address(0)) {
circulatingSupply = SafeMath.add(circulatingSupply, zcxToken.balanceOf(burnAddresses[i]));
}
}
}
function getDescriptiveCirculatingSupply() external view returns(uint256, uint256, string[] memory, uint256[] memory) {
uint256 totalSupply = zcxToken.totalSupply();
uint256 circulatingSupply = totalSupply;
string[] memory descriptions = new string[](lockedAddressCount + 1);
uint256[] memory supplies = new uint256[](lockedAddressCount + 1);
uint8 _idx;
for(uint8 i = 0; i < _lockIdx; i++) {
if(lockedAddresses[i].lockAddress != address(0)) {
descriptions[_idx] = lockedAddresses[i].name;
supplies[_idx] = zcxToken.balanceOf(lockedAddresses[i].lockAddress);
circulatingSupply = SafeMath.sub(circulatingSupply, supplies[_idx]);
_idx++;
}
}
descriptions[lockedAddressCount] = "Burned";
for(uint8 i = 0; i < _burnIdx; i++) {
if(burnAddresses[i] != address(0)) {
supplies[lockedAddressCount] = SafeMath.add(supplies[lockedAddressCount], zcxToken.balanceOf(burnAddresses[i]));
}
}
circulatingSupply = SafeMath.sub(circulatingSupply, supplies[lockedAddressCount]);
return (totalSupply,circulatingSupply, descriptions, supplies);
}
function getLockedAddresses() public view returns(address[] memory, string[] memory) {
address[] memory addresses = new address[](lockedAddressCount);
string[] memory descriptions = new string[](lockedAddressCount);
uint8 _idx;
for(uint8 i = 0; i < _lockIdx; i++) {
if(lockedAddresses[i].lockAddress != address(0)) {
addresses[_idx] = lockedAddresses[i].lockAddress;
descriptions[_idx] = lockedAddresses[i].name;
_idx++;
}
}
return (addresses, descriptions);
}
function getBurnAddress() public view returns(address[] memory) {
address[] memory addresses = new address[](burnAddressCount);
uint8 _idx;
for(uint8 i = 0; i < _burnIdx; i++) {
if(burnAddresses[i] != address(0)) {
addresses[_idx] = burnAddresses[i];
_idx++;
}
}
return addresses;
}
/* === CONTROL FUNCTIONS === */
function addBurnAddress(address _address) external onlyOwner {
uint8 _idx = _burnAddressExists(_address);
require(_idx == 255, "ALREADY_EXISTS");
burnAddresses[_burnIdx] = _address;
_burnIdx++;
burnAddressCount++;
}
function removeBurnAddress(address _address) external onlyOwner {
uint8 _idx = _burnAddressExists(_address);
require(_idx < 255, "DOES_NOT_EXIST");
burnAddresses[_idx] = address(0);
burnAddressCount--;
}
function addLockedAddress(address _address, string calldata _description) external onlyOwner {
uint8 _idx = _lockedAddressExists(_address);
require(_idx == 255, "ALREADY_EXISTS");
AddressInfo storage info = lockedAddresses[_lockIdx];
info.lockAddress = _address;
info.name = _description;
lockedAddressCount++;
_lockIdx++;
}
function removeLockedAddress(address _address) external onlyOwner {
uint8 _idx = _lockedAddressExists(_address);
require(_idx < 255, "DOES_NOT_EXIST");
lockedAddresses[_idx].lockAddress = address(0);
lockedAddressCount--;
}
function updateLockeAddressDescription(address _address, string calldata _newDescription) external onlyOwner {
uint8 _idx = _lockedAddressExists(_address);
require(_idx < 255, "DOES_NOT_EXIST");
lockedAddresses[_idx].name = _newDescription;
}
/* === INTERNAL FUNCTIONS === */
function _lockedAddressExists(address _address) internal view returns(uint8) {
if(lockedAddressCount == 0) return 255;
for(uint8 i = 0; i < _lockIdx; i++) {
if(lockedAddresses[i].lockAddress == _address) {
return i;
}
}
return 255;
}
function _burnAddressExists(address _address) internal view returns(uint8) {
if(burnAddressCount == 0) return 255;
for(uint8 i = 0; i < _burnIdx; i++) {
if(burnAddresses[i] == _address) {
return i;
}
}
return 255;
}
/* === EVENTS === */
event AddressAdded(address indexed _address, string _description);
event AddressRemoved(address indexed _address, string _description);
event AddressUpdated(address indexed _address, string _oldDescription, string _newDescription);
}
{
"compilationTarget": {
"contracts/UnizenSupply.sol": "UnizenRealCirculatingSupply"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 400
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_address","type":"address"},{"indexed":false,"internalType":"string","name":"_description","type":"string"}],"name":"AddressAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_address","type":"address"},{"indexed":false,"internalType":"string","name":"_description","type":"string"}],"name":"AddressRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_address","type":"address"},{"indexed":false,"internalType":"string","name":"_oldDescription","type":"string"},{"indexed":false,"internalType":"string","name":"_newDescription","type":"string"}],"name":"AddressUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"addBurnAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"},{"internalType":"string","name":"_description","type":"string"}],"name":"addLockedAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"burnAddressCount","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"","type":"uint8"}],"name":"burnAddresses","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBurnAddress","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCirculatingSupply","outputs":[{"internalType":"uint256","name":"circulatingSupply","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDescriptiveCirculatingSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string[]","name":"","type":"string[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLockedAddresses","outputs":[{"internalType":"address[]","name":"","type":"address[]"},{"internalType":"string[]","name":"","type":"string[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockedAddressCount","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"","type":"uint8"}],"name":"lockedAddresses","outputs":[{"internalType":"address","name":"lockAddress","type":"address"},{"internalType":"string","name":"name","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"removeBurnAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"removeLockedAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"},{"internalType":"string","name":"_newDescription","type":"string"}],"name":"updateLockeAddressDescription","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"zcxToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"}]