From 6ba698480a82fd8bae904d1fdddbf159b200887f Mon Sep 17 00:00:00 2001 From: mingda Date: Sat, 5 Dec 2020 23:23:23 +0800 Subject: [PATCH] ERC20 factory --- contracts/Factory/ERC20Factory.sol | 70 +++++++++++++ .../external/ERC20/InitializableERC20.sol | 83 ++++++++++++++++ .../ERC20/InitializableMintableERC20.sol | 97 +++++++++++++++++++ 3 files changed, 250 insertions(+) create mode 100644 contracts/Factory/ERC20Factory.sol create mode 100644 contracts/external/ERC20/InitializableERC20.sol create mode 100644 contracts/external/ERC20/InitializableMintableERC20.sol diff --git a/contracts/Factory/ERC20Factory.sol b/contracts/Factory/ERC20Factory.sol new file mode 100644 index 0000000..53f166f --- /dev/null +++ b/contracts/Factory/ERC20Factory.sol @@ -0,0 +1,70 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +import {Ownable} from "../lib/Ownable.sol"; +import {ICloneFactory} from "../lib/CloneFactory.sol"; +import {IConstFeeRateModel} from "../lib/ConstFeeRateModel.sol"; +import {IDVM} from "../DODOVendingMachine/intf/IDVM.sol"; +import {IDVMAdmin} from "../DODOVendingMachine/intf/IDVMAdmin.sol"; +import {IPermissionManager} from "../lib/PermissionManager.sol"; +import {InitializableERC20} from "../external/ERC20/InitializableERC20.sol"; +import {InitializableMintableERC20} from "../external/ERC20/InitializableMintableERC20.sol"; + +contract ERC20Factory is Ownable { + // ============ Templates ============ + + address public _CLONE_FACTORY_; + address public _ERC20_TEMPLATE_; + address public _MINTABLE_ERC20_TEMPLATE_; + + // ============ Events ============ + + event NewERC20(address indexed erc20, address indexed creator, bool isMintable); + + // ============ Functions ============ + + constructor( + address cloneFactory, + address erc20Template, + address mintableErc20Template + ) public { + _CLONE_FACTORY_ = cloneFactory; + _ERC20_TEMPLATE_ = erc20Template; + _MINTABLE_ERC20_TEMPLATE_ = mintableErc20Template; + } + + function createStdERC20( + uint256 totalSupply, + string memory name, + string memory symbol, + uint256 decimals + ) external returns (address newERC20) { + newERC20 = ICloneFactory(_CLONE_FACTORY_).clone(_ERC20_TEMPLATE_); + InitializableERC20(newERC20).init(msg.sender, totalSupply, name, symbol, decimals); + emit NewERC20(newERC20, msg.sender, false); + } + + function createMintableERC20( + uint256 initSupply, + string memory name, + string memory symbol, + uint256 decimals + ) external returns (address newMintableERC20) { + newMintableERC20 = ICloneFactory(_CLONE_FACTORY_).clone(_MINTABLE_ERC20_TEMPLATE_); + InitializableMintableERC20(newMintableERC20).init( + msg.sender, + initSupply, + name, + symbol, + decimals + ); + emit NewERC20(newMintableERC20, msg.sender, true); + } +} diff --git a/contracts/external/ERC20/InitializableERC20.sol b/contracts/external/ERC20/InitializableERC20.sol new file mode 100644 index 0000000..31310b2 --- /dev/null +++ b/contracts/external/ERC20/InitializableERC20.sol @@ -0,0 +1,83 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; + +import {SafeMath} from "../../lib/SafeMath.sol"; + +contract InitializableERC20 { + using SafeMath for uint256; + + string public name; + uint256 public decimals; + string public symbol; + uint256 public totalSupply; + + bool public initialized; + + mapping(address => uint256) balances; + mapping(address => mapping(address => uint256)) internal allowed; + + event Transfer(address indexed from, address indexed to, uint256 amount); + event Approval(address indexed owner, address indexed spender, uint256 amount); + + function init( + address _creator, + uint256 _totalSupply, + string memory _name, + string memory _symbol, + uint256 _decimals + ) public { + require(!initialized, "TOKEN_INITIALIZED"); + initialized = true; + totalSupply = _totalSupply; + balances[_creator] = _totalSupply; + name = _name; + symbol = _symbol; + decimals = _decimals; + } + + function transfer(address to, uint256 amount) public returns (bool) { + require(to != address(0), "TO_ADDRESS_IS_EMPTY"); + require(amount <= balances[msg.sender], "BALANCE_NOT_ENOUGH"); + + balances[msg.sender] = balances[msg.sender].sub(amount); + balances[to] = balances[to].add(amount); + emit Transfer(msg.sender, to, amount); + return true; + } + + function balanceOf(address owner) public view returns (uint256 balance) { + return balances[owner]; + } + + function transferFrom( + address from, + address to, + uint256 amount + ) public returns (bool) { + require(to != address(0), "TO_ADDRESS_IS_EMPTY"); + require(amount <= balances[from], "BALANCE_NOT_ENOUGH"); + require(amount <= allowed[from][msg.sender], "ALLOWANCE_NOT_ENOUGH"); + + balances[from] = balances[from].sub(amount); + balances[to] = balances[to].add(amount); + allowed[from][msg.sender] = allowed[from][msg.sender].sub(amount); + emit Transfer(from, to, amount); + return true; + } + + function approve(address spender, uint256 amount) public returns (bool) { + allowed[msg.sender][spender] = amount; + emit Approval(msg.sender, spender, amount); + return true; + } + + function allowance(address owner, address spender) public view returns (uint256) { + return allowed[owner][spender]; + } +} diff --git a/contracts/external/ERC20/InitializableMintableERC20.sol b/contracts/external/ERC20/InitializableMintableERC20.sol new file mode 100644 index 0000000..9cf6274 --- /dev/null +++ b/contracts/external/ERC20/InitializableMintableERC20.sol @@ -0,0 +1,97 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; + +import {SafeMath} from "../../lib/SafeMath.sol"; +import {InitializableOwnable} from "../../lib/InitializableOwnable.sol"; + +contract InitializableMintableERC20 is InitializableOwnable { + using SafeMath for uint256; + + string public name; + uint256 public decimals; + string public symbol; + uint256 public totalSupply; + + mapping(address => uint256) balances; + mapping(address => mapping(address => uint256)) internal allowed; + + event Transfer(address indexed from, address indexed to, uint256 amount); + event Approval(address indexed owner, address indexed spender, uint256 amount); + event Mint(address indexed user, uint256 value); + event Burn(address indexed user, uint256 value); + + function init( + address _creator, + uint256 _initSupply, + string memory _name, + string memory _symbol, + uint256 _decimals + ) public { + initOwner(_creator); + name = _name; + symbol = _symbol; + decimals = _decimals; + totalSupply = _initSupply; + balances[_creator] = _initSupply; + } + + function transfer(address to, uint256 amount) public returns (bool) { + require(to != address(0), "TO_ADDRESS_IS_EMPTY"); + require(amount <= balances[msg.sender], "BALANCE_NOT_ENOUGH"); + + balances[msg.sender] = balances[msg.sender].sub(amount); + balances[to] = balances[to].add(amount); + emit Transfer(msg.sender, to, amount); + return true; + } + + function balanceOf(address owner) public view returns (uint256 balance) { + return balances[owner]; + } + + function transferFrom( + address from, + address to, + uint256 amount + ) public returns (bool) { + require(to != address(0), "TO_ADDRESS_IS_EMPTY"); + require(amount <= balances[from], "BALANCE_NOT_ENOUGH"); + require(amount <= allowed[from][msg.sender], "ALLOWANCE_NOT_ENOUGH"); + + balances[from] = balances[from].sub(amount); + balances[to] = balances[to].add(amount); + allowed[from][msg.sender] = allowed[from][msg.sender].sub(amount); + emit Transfer(from, to, amount); + return true; + } + + function approve(address spender, uint256 amount) public returns (bool) { + allowed[msg.sender][spender] = amount; + emit Approval(msg.sender, spender, amount); + return true; + } + + function allowance(address owner, address spender) public view returns (uint256) { + return allowed[owner][spender]; + } + + function mint(address user, uint256 value) external onlyOwner { + balances[user] = balances[user].add(value); + totalSupply = totalSupply.add(value); + emit Mint(user, value); + emit Transfer(address(0), user, value); + } + + function burn(address user, uint256 value) external onlyOwner { + balances[user] = balances[user].sub(value); + totalSupply = totalSupply.sub(value); + emit Burn(user, value); + emit Transfer(user, address(0), value); + } +}