Last active
February 6, 2022 06:48
-
-
Save genecyber/d4b5072950ae6964c88dfe7d588c2e82 to your computer and use it in GitHub Desktop.
Configurable ERC20 Solidity Contract
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
_____ __ _ _ _ | |
/ ____| / _(_) | | | | | |
| | ___ _ __ | |_ _ __ _ _ _ _ __ __ _| |__ | | ___ | |
| | / _ \| '_ \| _| |/ _` | | | | '__/ _` | '_ \| |/ _ \ | |
| |___| (_) | | | | | | | (_| | |_| | | | (_| | |_) | | __/ | |
\_____\___/|_| |_|_| |_|\__, |\__,_|_| \__,_|_.__/|_|\___| | |
__/ | | |
______ _____ _____ ___|___/_ | |
| ____| __ \ / ____|__ \ / _ \ | |
| |__ | |__) | | ) | | | | | |
| __| | _ /| | / /| | | | | |
| |____| | \ \| |____ / /_| |_| | | |
|______|_| \_\\_____|____|\___/ | |
By the team that brought you: | |
--- > Circuits of Value (http://circuitsofvalue.com) | |
--- > Emblem Vault (https://emblem.finance) | |
Documentation: | |
--- > Github (https://gist.github.com/genecyber/d4b5072950ae6964c88dfe7d588c2e82) | |
*/ | |
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.6.12; | |
interface IERC20 { | |
function totalSupply() external view returns (uint); | |
function balanceOf(address account) external view returns (uint); | |
function transfer(address recipient, uint amount) external returns (bool); | |
function allowance(address owner, address spender) external view returns (uint); | |
function approve(address spender, uint amount) external returns (bool); | |
function transferFrom(address sender, address recipient, uint amount) external returns (bool); | |
event Transfer(address indexed from, address indexed to, uint value); | |
event Approval(address indexed owner, address indexed spender, uint value); | |
} | |
abstract contract ERC20Detailed is IERC20 { | |
string public name; | |
string public symbol; | |
uint8 public decimals; | |
constructor (string memory _name, string memory _symbol, uint8 _decimals) public { | |
name = _name; | |
symbol = _symbol; | |
decimals = _decimals; | |
} | |
} | |
contract Context { | |
constructor () internal { } | |
// solhint-disable-previous-line no-empty-blocks | |
function _msgSender() internal view returns (address payable) { | |
return msg.sender; | |
} | |
} | |
contract Configurable is Context { | |
using SafeMath for uint; | |
address private governance; | |
bool private _transferable = true; | |
bool private _burnable = true; | |
bool private _visible = true; | |
bool private _allowPrivateTransactions = false; | |
bool private _locked = false; | |
bool private _forever = false; | |
uint256 private _lockBlock = 0; | |
mapping (address => bool) public minters; | |
mapping (address => bool) public viewers; | |
function _isGoverner() internal view returns (bool) { | |
return _msgSender() == governance; | |
} | |
function _isViewer() internal view returns (bool) { | |
return viewers[_msgSender()]; | |
} | |
function _isMinter() internal view returns (bool) { | |
return minters[_msgSender()]; | |
} | |
function transferable() public view returns (bool) { | |
return _transferable; | |
} | |
function burnable() public view returns (bool) { | |
return _burnable; | |
} | |
function visible() public view returns (bool) { | |
return _visible; | |
} | |
function visibleOrAdmin() public view returns (bool) { | |
return _visible || _isGoverner(); | |
} | |
function allowPrivateTransactions() public view returns (bool) { | |
return _allowPrivateTransactions; | |
} | |
function blockNumberLocked() public view returns (bool) { | |
return _lockBlock != 0 && block.number < _lockBlock; | |
} | |
function locked() public view returns (bool) { | |
return _locked || blockNumberLocked(); | |
} | |
function lockedPermenantly() public view returns (bool) { | |
return locked() && _forever; | |
} | |
function blocksTillUnlock() public view returns (uint) { | |
if (_lockBlock > block.number) { | |
return _lockBlock.sub(block.number); | |
} else { | |
return 0; | |
} | |
} | |
modifier isTransferable() { | |
require(_transferable, 'Contract does not allow transfering'); | |
_; | |
} | |
modifier isBurnable() { | |
require(_burnable, 'Contract does not allow burning'); | |
_; | |
} | |
modifier isVisibleOrCanView() { | |
require(_visible || _isViewer() || _isGoverner(), 'Contract is private and you are not Governer or on viewers list'); | |
_; | |
} | |
modifier canSendPrivateOrGoverner() { | |
require(_allowPrivateTransactions || _isGoverner(), 'Contract cannot send private transactions'); | |
_; | |
} | |
modifier onlyOwner() { | |
require(_isGoverner(), "Sender is not Governer"); | |
_; | |
} | |
modifier notLocked() { | |
require(!locked(), 'Contract is locked to governance changes'); | |
_; | |
} | |
modifier canMint() { | |
require(_isMinter(), 'No Minting Privilages'); | |
_; | |
} | |
function unLock() public onlyOwner { | |
require(!lockedPermenantly(), 'Contract locked forever to governance changes'); | |
require(!blockNumberLocked(), 'Contract has been locked until a blocknumber'); | |
require(locked(), 'Contract not locked'); | |
_locked = false; | |
} | |
function lockForever() public onlyOwner { | |
require(!lockedPermenantly(), 'Contract locked forever to governance changes'); | |
require(!blockNumberLocked(), 'Contract has been locked until a blocknumber'); | |
_locked = true; | |
_forever = true; | |
} | |
function lockTemporarily() public onlyOwner notLocked { | |
_locked = true; | |
} | |
function lockTemporarilyTillBlock(uint blockNumber) public onlyOwner notLocked { | |
require(block.number < blockNumber, 'Provided Block numbner is in the past'); | |
_lockBlock = blockNumber; | |
} | |
function toggleBurnable() public onlyOwner notLocked { | |
_burnable = !_burnable; | |
} | |
function toggleTransferable() public onlyOwner notLocked { | |
_transferable = !_transferable; | |
} | |
function toggleVisibility() public onlyOwner notLocked { | |
_visible = !_visible; | |
} | |
function togglePrivateTransferability() public onlyOwner notLocked { | |
_allowPrivateTransactions = !_allowPrivateTransactions; | |
} | |
function setGovernance(address _governance) public onlyOwner notLocked { | |
_setGovernance(_governance); | |
} | |
function _setGovernance(address _governance) internal { | |
minters[governance] = false; // Remove old owner from minters list | |
viewers[governance] = false; // Remove old owner from viewers list | |
minters[_governance] = true; // Add new owner to minters list | |
viewers[_governance] = true; // Add new owner to viewers list | |
governance = _governance; // Set new owner | |
} | |
} | |
contract ERC20 is IERC20, Configurable { | |
using SafeMath for uint; | |
mapping (address => uint) private _balances; | |
mapping (address => mapping (address => uint)) private _allowances; | |
uint private _totalSupply; | |
function totalSupply() public view override isVisibleOrCanView returns (uint) { | |
return _totalSupply; | |
} | |
function balanceOf(address account) public view override isVisibleOrCanView returns (uint) { | |
return _balances[account]; | |
} | |
function allowance(address owner, address spender) public view override returns (uint) { | |
return _allowances[owner][spender]; | |
} | |
function approve(address spender, uint amount) public override returns (bool) { | |
_approve(_msgSender(), spender, amount); | |
return true; | |
} | |
function transferFrom(address sender, address recipient, uint amount) public override isTransferable returns (bool) { | |
_transferFromPrivate(sender, recipient, amount, visible()); | |
_approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); | |
return true; | |
} | |
function _transferFromPrivate(address sender, address recipient, uint amount, bool _private) internal isTransferable returns (bool) { | |
_transferPrivate(sender, recipient, amount, _private); | |
_approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); | |
return true; | |
} | |
function increaseAllowance(address spender, uint addedValue) public returns (bool) { | |
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); | |
return true; | |
} | |
function decreaseAllowance(address spender, uint subtractedValue) public returns (bool) { | |
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); | |
return true; | |
} | |
function transfer(address recipient, uint amount) public override isTransferable returns (bool) { | |
_transfer(_msgSender(), recipient, amount); | |
return true; | |
} | |
function _transfer(address sender, address recipient, uint amount) internal isTransferable { | |
_transferPrivate(sender, recipient, amount, visible()); | |
} | |
function _transferPrivate(address sender, address recipient, uint amount, bool _private) internal isTransferable { | |
require(sender != address(0), "ERC20: transfer from the zero address"); | |
require(recipient != address(0), "ERC20: transfer to the zero address"); | |
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); | |
_balances[recipient] = _balances[recipient].add(amount); | |
if (!_private) { | |
emit Transfer(sender, recipient, amount); | |
} | |
} | |
function _mint(address account, uint amount) internal { | |
require(account != address(0), "ERC20: mint to the zero address"); | |
_totalSupply = _totalSupply.add(amount); | |
_balances[account] = _balances[account].add(amount); | |
if (visible()) { | |
emit Transfer(address(0), account, amount); | |
} | |
} | |
function _burn(address account, uint amount) internal { | |
require(account != address(0), "ERC20: burn from the zero address"); | |
_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); | |
_totalSupply = _totalSupply.sub(amount); | |
if (visible()) { | |
emit Transfer(account, address(0), amount); | |
} | |
} | |
function _approve(address owner, address spender, uint amount) internal { | |
require(owner != address(0), "ERC20: approve from the zero address"); | |
require(spender != address(0), "ERC20: approve to the zero address"); | |
_allowances[owner][spender] = amount; | |
if (visible()) { | |
emit Approval(owner, spender, amount); | |
} | |
} | |
} | |
library SafeMath { | |
function add(uint a, uint b) internal pure returns (uint) { | |
uint c = a + b; | |
require(c >= a, "SafeMath: addition overflow"); | |
return c; | |
} | |
function sub(uint a, uint b) internal pure returns (uint) { | |
return sub(a, b, "SafeMath: subtraction overflow"); | |
} | |
function sub(uint a, uint b, string memory errorMessage) internal pure returns (uint) { | |
require(b <= a, errorMessage); | |
uint c = a - b; | |
return c; | |
} | |
function mul(uint a, uint b) internal pure returns (uint) { | |
if (a == 0) { | |
return 0; | |
} | |
uint c = a * b; | |
require(c / a == b, "SafeMath: multiplication overflow"); | |
return c; | |
} | |
function div(uint a, uint b) internal pure returns (uint) { | |
return div(a, b, "SafeMath: division by zero"); | |
} | |
function div(uint a, uint b, string memory errorMessage) internal pure returns (uint) { | |
// Solidity only automatically asserts when dividing by 0 | |
require(b > 0, errorMessage); | |
uint c = a / b; | |
return c; | |
} | |
} | |
library Address { | |
function isContract(address account) internal view returns (bool) { | |
bytes32 codehash; | |
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; | |
// solhint-disable-next-line no-inline-assembly | |
assembly { codehash := extcodehash(account) } | |
return (codehash != 0x0 && codehash != accountHash); | |
} | |
} | |
library SafeERC20 { | |
using SafeMath for uint; | |
using Address for address; | |
function safeTransfer(IERC20 token, address to, uint value) internal { | |
callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); | |
} | |
function safeTransferFrom(IERC20 token, address from, address to, uint value) internal { | |
callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); | |
} | |
function safeApprove(IERC20 token, address spender, uint value) internal { | |
require((value == 0) || (token.allowance(address(this), spender) == 0), | |
"SafeERC20: approve from non-zero to non-zero allowance" | |
); | |
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); | |
} | |
function callOptionalReturn(IERC20 token, bytes memory data) private { | |
require(address(token).isContract(), "SafeERC20: call to non-contract"); | |
// solhint-disable-next-line avoid-low-level-calls | |
(bool success, bytes memory returndata) = address(token).call(data); | |
require(success, "SafeERC20: low-level call failed"); | |
if (returndata.length > 0) { // Return data is optional | |
// solhint-disable-next-line max-line-length | |
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); | |
} | |
} | |
} | |
contract ConfigurableERC20 is ERC20, ERC20Detailed { | |
using SafeERC20 for IERC20; | |
using Address for address; | |
using SafeMath for uint; | |
constructor () public ERC20Detailed("Change Me", "CHANGE", 18) { | |
_setGovernance(_msgSender()); | |
} | |
function transfer(address to, uint amount, bool _private ) public isTransferable canSendPrivateOrGoverner { | |
_transferPrivate(_msgSender(), to, amount, _private); | |
} | |
function transferFrom(address from, address to, uint amount, bool _private ) public isTransferable canSendPrivateOrGoverner { | |
_transferPrivate(from, to, amount, _private); | |
} | |
function mint(address account, uint amount) public canMint notLocked { | |
_mint(account, amount); | |
} | |
function burn(uint amount) public isBurnable { | |
_burn(_msgSender(), amount); | |
} | |
function changeContractDetails(string memory _name, string memory _symbol, uint8 _decimals) public onlyOwner notLocked { | |
name = _name; | |
symbol = _symbol; | |
decimals = _decimals; | |
} | |
function addMinter(address _minter) public onlyOwner notLocked { | |
minters[_minter] = true; | |
} | |
function removeMinter(address _minter) public onlyOwner notLocked { | |
minters[_minter] = false; | |
} | |
function addViewer(address _viewer) public onlyOwner notLocked { | |
viewers[_viewer] = true; | |
} | |
function removeViewer(address _viewer) public onlyOwner notLocked { | |
viewers[_viewer] = false; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment