Last active
March 1, 2022 17:37
-
-
Save nhancv/58997e9a6e7f896627339d122e5671bf to your computer and use it in GitHub Desktop.
ERC20, BEP20, TRC20 Token template
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
// SPDX-License-Identifier: MIT | |
pragma solidity 0.8.3; | |
import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | |
import "./interfaces/IBurnable.sol"; | |
import "./ERC20Token.sol"; | |
/** | |
Function to receive approval and execute function in one call. | |
*/ | |
abstract contract TokenRecipient { | |
function receiveApproval(address _from, uint256 _value, address _token, bytes memory _extraData) virtual public; | |
} | |
/** | |
ERC20FullToken | |
- Burnable | |
- Allow receive and approval in one call | |
- Mintable | |
- Exchangeable | |
*/ | |
contract ERC20FullToken is ERC20Token, IBurnable { | |
constructor(string memory name_, string memory symbol_, uint8 decimals_, uint256 initialSupply_) | |
ERC20Token(name_, symbol_, decimals_, initialSupply_) {} | |
// Owner can transfer out any accidentally sent ERC20 tokens | |
function transferAnyERC20Token(address tokenAddress, uint tokens) public onlyOwner returns (bool success) { | |
return IERC20(tokenAddress).transfer(owner(), tokens); | |
} | |
// Approves and then calls the receiving contract | |
function approveAndCall(address _spender, uint256 _value, bytes memory _extraData) public returns (bool success) { | |
TokenRecipient spender = TokenRecipient(_spender); | |
approve(_spender, _value); | |
spender.receiveApproval(_msgSender(), _value, address(this), _extraData); | |
return true; | |
} | |
// Owner can mint more coin. | |
function mint(address _to, uint256 _amount) public onlyOwner { | |
_mint(_to, _amount); | |
} | |
// Handle if ether is sent to this address | |
receive() external payable { | |
// In-case: None Exchangeable | |
// If ether is sent to this address, send it back. | |
//revert(); | |
// In-case: Exchangeable | |
// Send 1 Eth to get 100 ExchangeableToken | |
uint256 _amountEth = msg.value; | |
require(_amountEth >= 1, "1 ETH to get 1000000 Token"); | |
uint256 _tokens = (_amountEth * 1000000 * 10 ** uint256(decimals())) / 1 ether; | |
_mint(_msgSender(), _tokens); | |
// Transfer ether to Owner | |
address payable payableOwner = payable(owner()); | |
(bool success,) = payableOwner.call{value : _amountEth}(""); | |
require(success, "Transfer failed."); | |
} | |
/** | |
* @dev Destroys `amount` tokens from `msg.sender`, reducing the total supply. | |
*/ | |
function burn(uint amount) override external { | |
require(presenter == _msgSender(), "N07Token: presenter only"); | |
_burn(_msgSender(), amount); | |
} | |
/** | |
* @dev Destroys `amount` tokens from `account`, deducting from the caller's allowance | |
*/ | |
function burnFrom(address account, uint amount) override external { | |
require(presenter == _msgSender(), "N07Token: presenter only"); | |
uint256 currentAllowance = allowance(account, _msgSender()); | |
require(currentAllowance >= amount, "N07Token: burn amount exceeds allowance"); | |
_approve(account, _msgSender(), currentAllowance - amount); | |
_burn(account, amount); | |
} | |
} | |
contract BUSD is ERC20FullToken { | |
constructor() ERC20FullToken("BUSD Token", "BUSD", 18, 10000000000) {} | |
} |
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
// SPDX-License-Identifier: MIT | |
pragma solidity 0.8.3; | |
import "@openzeppelin/contracts/access/Ownable.sol"; | |
import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; | |
import "./interfaces/ITokenPresenter.sol"; | |
/** | |
ERC20Token implementation | |
*/ | |
contract ERC20Token is ERC20, Ownable { | |
address public presenter; | |
uint8 private _decimals; | |
constructor(string memory name_, string memory symbol_, uint8 decimals_, uint256 initialSupply_) ERC20(name_, symbol_) { | |
_decimals = decimals_; | |
_mint(_msgSender(), initialSupply_ * 10 ** uint256(decimals_)); | |
} | |
/** | |
* @dev set the decimal | |
*/ | |
function decimals() override public view returns (uint8) { | |
return _decimals; | |
} | |
/** | |
* @dev set the presenter of the token to decide transfer functionality | |
* @param _presenter address of presenter | |
*/ | |
function setPresenter(address _presenter) onlyOwner public { | |
presenter = _presenter; | |
} | |
/** | |
* @dev transfer the tokens, if presenter is not set, normal behaviour | |
*/ | |
function _transfer(address _from, address _to, uint256 _amount) internal override { | |
// Transfer fund and responsibility to presenter | |
if (presenter != address(0) && presenter != _msgSender()) { | |
super._transfer(_from, presenter, _amount); | |
ITokenPresenter(presenter).receiveTokens(_msgSender(), _from, _to, _amount); | |
} else { | |
super._transfer(_from, _to, _amount); | |
} | |
} | |
} |
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
// SPDX-License-Identifier: MIT | |
pragma solidity 0.8.3; | |
interface IBurnable { | |
function burn(uint amount) external; | |
function burnFrom(address account, uint amount) external; | |
} |
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
// SPDX-License-Identifier: MIT | |
pragma solidity 0.8.3; | |
interface ITokenPresenter { | |
function receiveTokens(address trigger, address _from, address _to, uint256 _amount) external returns (bool); | |
} |
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
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.3; | |
import "@openzeppelin/contracts/access/Ownable.sol"; | |
contract Maintainable is Ownable { | |
bool public isMaintenance = false; | |
bool public isOutdated = false; | |
// Check if contract is not in maintenance | |
function ifNotMaintenance() internal view { | |
require(!isMaintenance, "Maintenance"); | |
require(!isOutdated, "Outdated"); | |
} | |
// Check if contract on maintenance for restore | |
function ifMaintenance() internal view { | |
require(isMaintenance, "!Maintenance"); | |
} | |
// Enable maintenance | |
function enableMaintenance(bool status) onlyOwner public { | |
isMaintenance = status; | |
} | |
// Enable outdated | |
function enableOutdated(bool status) onlyOwner public { | |
isOutdated = status; | |
} | |
} | |
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
const BN = web3.utils.BN; | |
const ERC20Token = artifacts.require("ERC20Token"); | |
const ERC20FullToken = artifacts.require("ERC20FullToken"); | |
contract('ERC20Token', ([owner, bob]) => { | |
it('should put 777999777 ERC20Token token in the first account', async () => { | |
const tokenInstance = await ERC20Token.deployed(); | |
const balance = await tokenInstance.balanceOf.call(owner); | |
assert.equal(balance.valueOf(), 777999777000000000000000000, "777999777000000000000000000 wasn't in the first account"); | |
}); | |
it('should send coin correctly', async () => { | |
const tokenInstance = await ERC20Token.deployed(); | |
// Get initial balances of first and second account. | |
const accountOneStartingBalance = await tokenInstance.balanceOf.call(owner); | |
const accountTwoStartingBalance = await tokenInstance.balanceOf.call(bob); | |
// Make transaction from first account to second. | |
const amount = 10; | |
await tokenInstance.transfer(bob, amount.toString()); | |
assert.equal((await tokenInstance.balanceOf.call(owner)).toString(), accountOneStartingBalance.sub(new BN(amount)).toString(), "Amount wasn't correctly taken from the sender"); | |
assert.equal((await tokenInstance.balanceOf.call(bob)).toString(), accountTwoStartingBalance.add(new BN(amount)).toString(), "Amount wasn't correctly sent to the receiver"); | |
}); | |
it('should create ERC20FullToken correctly', async () => { | |
const tokenInstance = await ERC20FullToken.new("Nhan Cao", "nhancv", 18, 777999777, {from: owner}); | |
// mintable | |
assert.equal((await tokenInstance.balanceOf.call(bob)).toString(), 0, "Bob's balance should be zero"); | |
await tokenInstance.mint(bob, 10, {from: owner}); | |
assert.equal((await tokenInstance.balanceOf.call(bob)).toString(), 10, "Bob's balance should be 10"); | |
// exchangeable | |
await tokenInstance.sendTransaction({from: bob, value: web3.utils.toWei('1.1', 'ether')}); | |
assert.equal((await tokenInstance.balanceOf.call(bob)).toString(), 1100000000000000000000010, "Bob's balance should be 1100000000000000000000010"); | |
// burnable | |
await tokenInstance.burn(10, {from: bob}); | |
assert.equal((await tokenInstance.balanceOf.call(bob)).toString(), 1100000000000000000000000, "Bob's balance should be 1100000000000000000000000"); | |
// burnable from | |
await tokenInstance.approve(owner, '1100000000000000000000000', {from: bob}); | |
await tokenInstance.burnFrom(bob, '1100000000000000000000000', {from: owner}); | |
assert.equal((await tokenInstance.balanceOf.call(bob)).toString(), 0, "Bob's balance should be 0"); | |
}); | |
}); |
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
// SPDX-License-Identifier: MIT | |
pragma solidity 0.8.3; | |
import "truffle/Assert.sol"; | |
import "truffle/DeployedAddresses.sol"; | |
import "../src/active/ERC20Token.sol"; | |
contract TestERC20Token { | |
function testInitialBalanceUsingDeployedContract() public { | |
ERC20Token meta = ERC20Token(DeployedAddresses.ERC20Token()); | |
uint expected = 777999777000000000000000000; | |
Assert.equal(meta.totalSupply(), expected, "Owner should have 777999777000000000000000000 ERC20Token initially"); | |
} | |
function testInitialBalanceWithNewERC20Token() public { | |
ERC20Token meta = new ERC20Token("Nhan Cao", "nhancv", 18, 777999777); | |
uint expected = 777999777000000000000000000; | |
Assert.equal(meta.balanceOf(meta.owner()), expected, "Owner should have 777999777000000000000000000 ERC20Token initially"); | |
} | |
} |
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
// SPDX-License-Identifier: MIT | |
pragma solidity 0.8.3; | |
import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | |
import "./interfaces/ITokenPresenter.sol"; | |
import "./utils/Maintainable.sol"; | |
import "./utils/EmergencyWithdraw.sol"; | |
contract TokenPresenter is ITokenPresenter, EmergencyWithdraw, Maintainable { | |
address public token; | |
constructor() { | |
token = address(0); | |
} | |
/** | |
* @dev set the main token | |
* @param _token address of main token | |
*/ | |
function setToken(address _token) onlyOwner public { | |
token = _token; | |
} | |
/** | |
* @dev this is the main function to distribute the tokens call from only main token via external app | |
* @param _trigger trigger address | |
* @param _from from address | |
* @param _to to address | |
* @param _amount amount of tokens | |
*/ | |
function receiveTokens(address _trigger, address _from, address _to, uint256 _amount) public override returns (bool) { | |
ifNotMaintenance(); | |
require(msg.sender == token, "TokenPresenter: Only trigger from token"); | |
IERC20(token).transfer(_to, _amount); | |
return true; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment