Skip to content

Instantly share code, notes, and snippets.

@nhancv
Last active March 1, 2022 17:37
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save nhancv/58997e9a6e7f896627339d122e5671bf to your computer and use it in GitHub Desktop.
Save nhancv/58997e9a6e7f896627339d122e5671bf to your computer and use it in GitHub Desktop.
ERC20, BEP20, TRC20 Token template
// 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) {}
}
// 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);
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.3;
interface IBurnable {
function burn(uint amount) external;
function burnFrom(address account, uint amount) external;
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.3;
interface ITokenPresenter {
function receiveTokens(address trigger, address _from, address _to, uint256 _amount) external returns (bool);
}
// 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;
}
}
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");
});
});
// 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");
}
}
// 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