Last active
April 25, 2023 23:43
-
-
Save tunnckoCore/841b55adfc8d75e45dd5052481babf8e to your computer and use it in GitHub Desktop.
Solidity v0.8 Token Deployer Factory for cheaply deploying secure ERC-20 Burnable tokens with no ownership. Allows for deploying with CREATE2 too. The ownership of the created token is renounced immediately after creation.
This file contains 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
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.18; | |
// Created by @tunnckoCore / @wgw_eth / wgw.eth | |
// Compatible with IERC20 (OpenZeppelin impl), IERC20Metadata, ERC20Burnable, Ownable | |
// The reason to be custom copy, is this a bit cheaper to deploy and i'm short on funds. | |
// ERC20Burnable and Ownable are also implemented and included at the end of the contract. | |
// There's also a public mint function, available only for the owner of the contract. | |
// But this contract is intented through WTFDeployer, | |
// which automatically renounces the ownership on deployment, | |
// so that no one owns it, even that deployer contract. | |
contract WTF20 { | |
address private _owner = msg.sender; | |
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); | |
event Transfer(address indexed from, address indexed to, uint256 value); | |
event Approval(address indexed owner, address indexed spender, uint256 value); | |
mapping(address => uint256) private _balances; | |
mapping(address => mapping(address => uint256)) private _allowances; | |
uint256 private _totalSupply; | |
string private _name; | |
string private _symbol; | |
constructor(string memory name_, string memory symbol_) { | |
_name = name_; | |
_symbol = symbol_; | |
} | |
function name() public view virtual returns (string memory) { | |
return _name; | |
} | |
function symbol() public view virtual returns (string memory) { | |
return _symbol; | |
} | |
function decimals() public view virtual returns (uint8) { | |
return 18; | |
} | |
function totalSupply() public view virtual returns (uint256) { | |
return _totalSupply; | |
} | |
function balanceOf(address account) public view virtual returns (uint256) { | |
return _balances[account]; | |
} | |
function transfer(address to, uint256 amount) public virtual returns (bool) { | |
address owner_ = msg.sender; | |
_transfer(owner_, to, amount); | |
return true; | |
} | |
function allowance(address owner_, address spender) public view virtual returns (uint256) { | |
return _allowances[owner_][spender]; | |
} | |
function approve(address spender, uint256 amount) public virtual returns (bool) { | |
address owner_ = msg.sender; | |
_approve(owner_, spender, amount); | |
return true; | |
} | |
function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) { | |
address spender = msg.sender; | |
_spendAllowance(from, spender, amount); | |
_transfer(from, to, amount); | |
return true; | |
} | |
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { | |
address owner_ = msg.sender; | |
_approve(owner_, spender, allowance(owner_, spender) + addedValue); | |
return true; | |
} | |
function decreaseAllowance( | |
address spender, | |
uint256 subtractedValue | |
) public virtual returns (bool) { | |
address owner_ = msg.sender; | |
uint256 currentAllowance = allowance(owner_, spender); | |
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); | |
unchecked { | |
_approve(owner_, spender, currentAllowance - subtractedValue); | |
} | |
return true; | |
} | |
function _transfer(address from, address to, uint256 amount) internal virtual { | |
require(from != address(0), "ERC20: transfer from the zero address"); | |
require(to != address(0), "ERC20: transfer to the zero address"); | |
// _beforeTokenTransfer(from, to, amount); | |
uint256 fromBalance = _balances[from]; | |
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); | |
unchecked { | |
_balances[from] = fromBalance - amount; | |
// Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by | |
// decrementing then incrementing. | |
_balances[to] += amount; | |
} | |
emit Transfer(from, to, amount); | |
// _afterTokenTransfer(from, to, amount); | |
} | |
function _mint(address account, uint256 amount) internal virtual { | |
require(account != address(0), "ERC20: mint to the zero address"); | |
// _beforeTokenTransfer(address(0), account, amount); | |
_totalSupply += amount; | |
unchecked { | |
// Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above. | |
_balances[account] += amount; | |
} | |
emit Transfer(address(0), account, amount); | |
// _afterTokenTransfer(address(0), account, amount); | |
} | |
function _burn(address account, uint256 amount) internal virtual { | |
require(account != address(0), "ERC20: burn from the zero address"); | |
// _beforeTokenTransfer(account, address(0), amount); | |
uint256 accountBalance = _balances[account]; | |
require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); | |
unchecked { | |
_balances[account] = accountBalance - amount; | |
// Overflow not possible: amount <= accountBalance <= totalSupply. | |
_totalSupply -= amount; | |
} | |
emit Transfer(account, address(0), amount); | |
// _afterTokenTransfer(account, address(0), amount); | |
} | |
function _approve(address owner_, address spender, uint256 amount) internal virtual { | |
require(owner_ != address(0), "ERC20: approve from the zero address"); | |
require(spender != address(0), "ERC20: approve to the zero address"); | |
_allowances[owner_][spender] = amount; | |
emit Approval(owner_, spender, amount); | |
} | |
function _spendAllowance(address owner_, address spender, uint256 amount) internal virtual { | |
uint256 currentAllowance = allowance(owner_, spender); | |
if (currentAllowance != type(uint256).max) { | |
require(currentAllowance >= amount, "ERC20: insufficient allowance"); | |
unchecked { | |
_approve(owner_, spender, currentAllowance - amount); | |
} | |
} | |
} | |
// ER20Burnable START | |
function burn(uint256 amount) public virtual { | |
_burn(msg.sender, amount); | |
} | |
function burnFrom(address account, uint256 amount) public virtual { | |
_spendAllowance(account, msg.sender, amount); | |
_burn(account, amount); | |
} | |
// ER20 Burnable END | |
// Minimal Ownable | |
function owner() public view virtual returns (address) { | |
return _owner; | |
} | |
modifier onlyOwner() { | |
require(owner() == msg.sender, "Ownable: caller is not the owner"); | |
_; | |
} | |
function renounceOwnership() public virtual onlyOwner { | |
_transferOwnership(address(0)); | |
} | |
function _transferOwnership(address newOwner) internal virtual { | |
address oldOwner = _owner; | |
_owner = newOwner; | |
emit OwnershipTransferred(oldOwner, newOwner); | |
} | |
// OWNABLE END | |
// Public mint function. It will not matter, because we immediately renounce ownership | |
function mint(address account, uint256 amount) public onlyOwner { | |
_mint(account, amount); | |
} | |
} |
This file contains 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
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.18; | |
// Created by @tunnckoCore / @wgw_eth / wgw.eth | |
import "./WTF20.sol"; | |
import "./BurnLotto.sol"; | |
contract WTFDeployer { | |
// the deployer of the TokenDepoyer | |
address public immutable deployer = msg.sender; | |
// prettier-ignore | |
bytes public SIGNATURE = bytes("0x8060c5c048f639d4726a858be8a3f57dd0d8c2fa87ecad46490ca7ebaf5de59964812750fdc6e2f9a6cbcd97b1d08ffdf488aa0e040e7cef8e7425d845462af11b"); | |
event ERC20TokenCreated( | |
address deployedAt, | |
string name, | |
string symbol, | |
uint256 supply, | |
address creator | |
); | |
event BurnLottoCreated(address deployedAt, address token, bytes signature); | |
function deployToken( | |
string calldata name, | |
string calldata symbol, | |
uint256 initialSupply | |
) public returns (address) { | |
return deployTokenCustom(name, symbol, initialSupply, 1234, false); | |
} | |
function deployTokenWithSalt( | |
string calldata name, | |
string calldata symbol, | |
uint256 initialSupply, | |
uint256 salt | |
) public returns (address) { | |
return deployTokenCustom(name, symbol, initialSupply, salt, false); | |
} | |
function deployTokenCustom( | |
string calldata name, | |
string calldata symbol, | |
uint256 initialSupply, | |
uint256 salt, | |
bool withLottery | |
) public returns (address) { | |
WTF20 token = new WTF20{salt: bytes32(salt)}(name, symbol); | |
uint256 supply = initialSupply * 10 ** WTF20(token).decimals(); | |
uint256 onePercent = supply / 100; | |
// Mint 1% to the service, 99% to the caller (creator/deployer of this token) | |
WTF20(token).mint(deployer, onePercent); | |
WTF20(token).mint(msg.sender, supply - onePercent); | |
// immediately renounce the ownership of the created token, | |
// so that no one owns it, even this deployer contract, nor the caller/creator/minter | |
WTF20(token).renounceOwnership(); | |
if (withLottery) { | |
deployBurnLotto(address(token)); | |
} | |
emit ERC20TokenCreated(address(token), name, symbol, supply, msg.sender); | |
return address(token); | |
} | |
function deployTokenWithLottery( | |
string calldata name, | |
string calldata symbol, | |
uint256 initialSupply | |
) public returns (address) { | |
return deployTokenCustom(name, symbol, initialSupply, 5848, true); | |
} | |
function deployBurnLotto(address token) public returns (address) { | |
BurnLotto lotto = new BurnLotto(token, SIGNATURE); | |
emit BurnLottoCreated(address(lotto), token, SIGNATURE); | |
return address(lotto); | |
} | |
function deployBurnLottoWithSignature( | |
address token, | |
bytes memory signature | |
) public returns (address) { | |
BurnLotto lotto = new BurnLotto(token, signature); | |
emit BurnLottoCreated(address(lotto), token, signature); | |
return address(lotto); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment