// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
contract NaughtCoin is ERC20 {
// string public constant name = 'NaughtCoin';
// string public constant symbol = '0x0';
// uint public constant decimals = 18;
uint public timeLock = now + 10 * 365 days;
uint256 public INITIAL_SUPPLY;
address public player;
constructor(address _player)
ERC20('NaughtCoin', '0x0')
public {
player = _player;
INITIAL_SUPPLY = 1000000 * (10**uint256(decimals()));
// _totalSupply = INITIAL_SUPPLY;
// _balances[player] = INITIAL_SUPPLY;
_mint(player, INITIAL_SUPPLY);
emit Transfer(address(0), player, INITIAL_SUPPLY);
}
function transfer(address _to, uint256 _value) override public lockTokens returns(bool) {
super.transfer(_to, _value);
}
// Prevent the initial owner from transferring tokens until the timelock has passed
modifier lockTokens() {
if (msg.sender == player) {
require(now > timeLock);
_;
} else {
_;
}
}
}
玩家必須將自己帳戶內的 Token 全數轉出才能通關 (Token 數量 == 0)
自從以太坊上的智能合約問世後,區塊鏈不只能做到儲存價值與匯款轉帳,更能以智能合約在鏈上創造各式各樣的應用程式,而今天的主旨 ERC20 便是其中之一,不過,在針對 ERC20 的功能講解之前,還是先來看看甚麼是 ERC20 吧 !
ERC20 是社群推薦開發者使用的標準,裡面定義了開發者需要為自己的合約完成的函數,和這些函數的名稱,我們甚至可以在 Opzeppelin 的 Github 裡面找到完整的 ERC20 的 Contract code,需要使用時能夠直接複製使用。
而 ERC20 是一個能夠讓開發者在合約裡發行自己的 Token 的合約,主要由一個 mapping 紀錄帳戶內的餘額
mapping(address => uint256) private _balances;
並且在為你的 Token 取名
uint256 private _totalSupply;
string private _name;
string private _symbol;
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
如此一來便有了一個欲發行的 Token 的基礎資料,最後再將一個 Token 該有的基本函數完成定義後即可(例如轉帳等等)
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
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);
}
approve 能夠許可某人存取自己的 Token
function transferFrom(
address from,
address to,
uint256 amount
) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
transferFrom/transfer 轉移 Token 的函數,可以讓使用者將 Token 轉移到指定的地址
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);
}
_mint 能夠製造(發行新的 Token) Token 至指定地址,通常會加上限制(你不會希望每個人都能隨便製造自己的 Token 吧)
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);
}
_burn 如同字面的意思,將 Token 燒毀(扣除發行總量 + 扣除某人持有量),就結果而言會減少 Token 的總量
以上是部分 ERC20 常使用到的函數,不過既然都介紹了,那倒不如就簡單發行一個吧(?)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract token is ERC20 {
constructor(string memory name, string memory symbol) ERC20(name, symbol) {}
function mint(uint amount) public {
_mint(msg.sender, amount);
}
}
把這份合約扔到測試網上面吧 (◑‿◐)
接著找找最近的一筆交易,找到自己部署的合約地址
找到合約地址後請點擊 Metamask 上的 import token
填上合約地址後點擊 Add Custom Token
這樣就能看到自己發行的 Token 拉 (◉ω◉)
接著我們回到 Remix 執行 mint
就能看到自己的帳戶底下有 Token 囉~
好啦,那希望大家都能順利發行自己的 Token,接著就進入 Level 15 的通關流程囉
import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
{...}
function transfer(address _to, uint256 _value) override public lockTokens returns(bool) {
super.transfer(_to, _value);
}
// Prevent the initial owner from transferring tokens until the timelock has passed
modifier lockTokens() {
if (msg.sender == player) {
require(now > timeLock);
_;
} else {
_;
}
}
可以看到 transfer 被 lockTokens 這個 modifier 給鎖住了,還鎖了 10 年啊 ( ゚д゚)
不過我們剛剛也看到了 Openzeppelin 的 ERC20 合約裡所實現的函數,發現有兩個 transfer 可以用,所以只要使用另外一個 transferFrom 轉出 Token 就可以囉。
player2 = "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"
balance = await contract.balanceOf(player).then(v => v.toString())
await contract.approve(player, balance)
await contract.transferFrom(player, player2, balance)
٩(^ ‿ ^)۶ ٩(^ ‿ ^)۶ ٩(^ ‿ ^)۶ ٩(^ ‿ ^)۶
https://docs.openzeppelin.com/contracts/4.x/erc20
https://docs.openzeppelin.com/contracts/4.x/api/token/erc20