今天刷Ethernaut遇到了一道ERC20的题目,考虑到自己还没有实际用过ERC20,以及它的编写,就花了一个晚上写了点代码,可以发现自己的币。
也许你经常看到ERC20和代币一同出现, ERC20是以太坊定义的一个代币标准。
要求我们在实现代币的时候必须要遵守的协议,如指定代币名称、总量、实现代币交易函数等,只有支持了协议才能被以太坊钱包支持。
登链社区关于ERC20的API规范:
代币标准
还有github的英文版标准:
代币标准
规范中也推荐了几个比较好的实现实例:
实例,我就是学习了这些实例,然后自己编写了一遍。
ERC20Interface.sol
:
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
interface ERC20Interface {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8 );
function totalSupply() external view returns (uint256 );
function balanceOf(address _owner) external view returns (uint256 balance);
function transfer(address _to, uint256 _value) external returns (bool success);
function transferFrom(address _from, address _to, uint256 _value) external returns (bool success);
function approve(address _spender, uint256 _value) external returns (bool success);
function allowance(address _owner, address _spender) external view returns (uint256 remaining);
}
接口函数,这些函数的原型也都是上面的代币标准中摘录下来的。对于每个函数的用途也都做了解释。
然后就是继承接口并编写ERC20.sol
:
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "./ERC20Interface.sol";
contract ERC20 is ERC20Interface{
string public _name = "Feng";
string public _symbol = "FENG";
uint8 public _decimals = 18; // 18 是建议的默认值
uint256 public _totalSupply;
mapping (address => uint256) public _balanceOf;
mapping (address => mapping (address => uint256)) _allowance;
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
function name() public view virtual override returns (string memory){
return _name;
}
function symbol() public view virtual override returns (string memory){
return _symbol;
}
function decimals() public view virtual override returns (uint8 ){
return _decimals;
}
function totalSupply() public view virtual override returns (uint256 ){
return _totalSupply;
}
function balanceOf(address _owner) public view virtual override returns (uint256 balance){
balance = _balanceOf[_owner];
}
function _transfer(address _from, address _to, uint256 _value) internal virtual {
require(_from != address(0));
require(_to != address(0));
require(_balanceOf[_from] >= _value);
_balanceOf[msg.sender]-=_value;
_balanceOf[_to]+=_value;
emit Transfer(_from, _to, _value);
}
function _approve(address _owner, address _spender, uint256 _value) internal virtual {
require(_owner != address(0));
require(_spender != address(0));
_allowance[_owner][_spender] = _value;
emit Approval(_owner, _spender, _value);
}
function transfer(address _to, uint256 _value) public virtual override returns (bool success){
_transfer(msg.sender, _to, _value);
success = true;
}
function transferFrom(address _from, address _to, uint256 _value) public virtual override returns (bool success){
_transfer(_from, _to, _value);
uint256 allowanceNum = _allowance[_from][_to];
require(allowanceNum >= _value);
_approve(_from, _to, allowanceNum - _value);
success = true;
}
function approve(address _spender, uint256 _value) public virtual override returns (bool success){
_approve(msg.sender, _spender, _value);
success = true;
}
function allowance(address _owner, address _spender) public view virtual override returns (uint256 remaining){
return _allowance[_owner][_spender];
}
function _mint(address _account, uint256 _value) internal virtual{
require(_account != address(0));
_totalSupply += _value;
_balanceOf[_account] += _value;
emit Transfer(address(0), _account, _value);
}
}
其中也会多出来一些函数是接口中没有的,例如_mint
,是铸币函数。
再自己写一个Fun.sol
来用一下自己写的代币玩玩:
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "./ERC20.sol";
contract Fun is ERC20{
address public owner;
constructor() payable {
owner = msg.sender;
}
modifier onlyOwner(){
require(msg.sender == owner);
_;
}
function mint(address _account, uint _value) public onlyOwner{
_mint(_account, _value);
}
function getMoney() public {
uint value = uint(keccak256(abi.encodePacked(block.timestamp))) % 10;
_mint(msg.sender, value);
}
}
即可使用了。需要注意那个_decimals = 18
,即有18位小数,因此一般的transfer这样的,如果value传1,其实是0.000000000000000001 FENG
,因此如果是1 FENG的话,value得是1000000000000000000
。
学习了一下ERC20,同时写了一些代码,对于代币以及合约的继承的理解更深了。