// Token - ERC20-style fungible token
// Demonstrates mappings, events, and transfers
contract Token {
// Token metadata
string public name;
string public symbol;
uint8 public decimals = 9;
// Token state
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
// Ownership
address public owner;
bool public paused;
// Events
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
event Mint(address indexed to, uint256 amount);
event Burn(address indexed from, uint256 amount);
event Paused(address account);
event Unpaused(address account);
// Errors
error InsufficientBalance(uint256 available, uint256 required);
error InsufficientAllowance(uint256 available, uint256 required);
error ContractPaused();
error Unauthorized();
error InvalidAddress();
// Modifiers
modifier onlyOwner() {
if (msg.sender != owner) revert Unauthorized();
_;
}
modifier whenNotPaused() {
if (paused) revert ContractPaused();
_;
}
modifier validAddress(address addr) {
if (addr == address(0)) revert InvalidAddress();
_;
}
// Constructor
constructor(string memory _name, string memory _symbol, uint256 _initialSupply) {
name = _name;
symbol = _symbol;
owner = msg.sender;
if (_initialSupply > 0) {
_mint(msg.sender, _initialSupply);
}
}
// Transfer tokens
function transfer(address to, uint256 amount)
public
whenNotPaused
validAddress(to)
returns (bool)
{
_transfer(msg.sender, to, amount);
return true;
}
// Approve spender
function approve(address spender, uint256 amount)
public
validAddress(spender)
returns (bool)
{
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
// Transfer from (using allowance)
function transferFrom(address from, address to, uint256 amount)
public
whenNotPaused
validAddress(to)
returns (bool)
{
uint256 currentAllowance = allowance[from][msg.sender];
if (currentAllowance < amount) {
revert InsufficientAllowance(currentAllowance, amount);
}
allowance[from][msg.sender] = currentAllowance - amount;
_transfer(from, to, amount);
return true;
}
// Mint new tokens (owner only)
function mint(address to, uint256 amount) public onlyOwner validAddress(to) {
_mint(to, amount);
emit Mint(to, amount);
}
// Burn tokens
function burn(uint256 amount) public {
if (balanceOf[msg.sender] < amount) {
revert InsufficientBalance(balanceOf[msg.sender], amount);
}
balanceOf[msg.sender] -= amount;
totalSupply -= amount;
emit Burn(msg.sender, amount);
emit Transfer(msg.sender, address(0), amount);
}
// Pause transfers (owner only)
function pause() public onlyOwner {
paused = true;
emit Paused(msg.sender);
}
// Unpause transfers (owner only)
function unpause() public onlyOwner {
paused = false;
emit Unpaused(msg.sender);
}
// Internal transfer
function _transfer(address from, address to, uint256 amount) internal {
if (balanceOf[from] < amount) {
revert InsufficientBalance(balanceOf[from], amount);
}
balanceOf[from] -= amount;
balanceOf[to] += amount;
emit Transfer(from, to, amount);
}
// Internal mint
function _mint(address to, uint256 amount) internal {
totalSupply += amount;
balanceOf[to] += amount;
emit Transfer(address(0), to, amount);
}
}