1. Soạn hợp đồng thông minh ERC-20
1.1 Cấu trúc tổng thể của MyERC20
pragma solidity ^0.5.0;
/**
* @dev Giao diện của chuẩn ERC20 theo định nghĩa trong EIP. Không chứa
* các hàm tùy chọn; xem `ERC20Detailed` để truy cập.
*/
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address tài khoản) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
library SafeMath {
/**
* @dev Trả lại tổng của hai số nguyên không dấu, hoàn ngược nếu có
* tràn số.
*
* Tương đương với toán tử `+` trong Solidity.
*
* Yêu cầu:
* - Phép cộng không được tràn số.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: phép cộng tràn số");
return c;
}
/**
* @dev Trả về hiệu của hai số nguyên không dấu, hoàn ngược nếu có
* tràn số (khi kết quả là số âm).
*
* Tương đương với toán tử `-` trong Solidity.
*
* Yêu cầu:
* - Phép trừ không được tràn số.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: phép trừ tràn số");
uint256 c = a - b;
return c;
}
/**
* @dev Trả về tích của hai số nguyên không dấu, hoàn ngược nếu có
* tràn số.
*
* Tương đương với toán tử `*` trong Solidity.
*
* Yêu cầu:
* - Phép nhân không được tràn số.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Tối ưu hóa phí ga: cách này rẻ hơn yêu cầu 'a' thay vì 0, nhưng
// lợi ích sẽ mất nếu 'b' cũng kiểm thử.
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: phép nhân tràn số");
return c;
}
/**
* @dev Trả về giá trị nguyên của phép chia hai số nguyên không dấu. Hoàn ngược nếu
* chia cho 0. Kết quả được làm tròn về 0.
*
* Tương đương với toán tử `/` trong Solidity. Lưu ý: hàm này sử dụng một
* mã vận hành `revert` (giữ lại toàn bộ gas còn lại) trong khi Solidity
* sử dụng một mã vận hành không hợp lệ để hoàn ngược (tiêu thụ toàn bộ gas còn lại).
*
* Yêu cầu:
* - Số chia không được bằng 0.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity chỉ tự động kiểm tra khi chia cho 0
require(b > 0, "SafeMath: số chia bằng 0");
uint256 c = a / b;
// assert(a == b * c + a % b); // Không có trường hợp nào mà điều này không đúng
return c;
}
/**
* @dev Trả về phần dư của phép chia hai số nguyên không dấu. (unsigned integer modulo),
* Hoàn ngược khi chia cho 0.
*
* Tương đương với toán tử `%` trong Solidity. Hàm này sử dụng một
* mã vận hành `revert` (giữ lại toàn bộ gas còn lại) trong khi Solidity
* sử dụng một mã vận hành không hợp lệ để hoàn ngược (tiêu thụ toàn bộ gas còn lại).
*
* Yêu cầu:
* - Số chia không được bằng 0.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, "SafeMath: số chia bằng 0");
return a % b;
}
}
/**
* @dev Triển khai giao diện `IERC20`.
*
* Lần triển khai này không phụ thuộc vào cách tạo token. Điều này có nghĩa là
* cơ chế cung cấp phải được thêm vào một hợp đồng phái sinh thông qua `_mint`.
* Để biết cơ chế tổng quát, xem `ERC20Mintable`.
*
* *Để biết thêm chi tiết, hãy xem hướng dẫn của chúng tôi [Cách triển khai
* cơ chế cung cấp](https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226).*
*
* Chúng tôi đã tuân thủ các hướng dẫn tổng quát của OpenZeppelin: các hàm hoàn ngược thay vì
* trả về `false` khi thất bại. Tuy nhiên, hành vi này khá phổ biến
* và không xung đột với các kỳ vọng của ứng dụng ERC20.
*
* Ngoài ra, sự kiện `Approval` được kích hoạt khi gọi `transferFrom`.
* Điều này cho phép ứng dụng xây dựng lại ủy quyền cho tất cả các tài khoản
* chỉ bằng cách nghe các sự kiện này. Các lần triển khai khác của EIP có thể không phát ra
* các sự kiện này vì không yêu cầu trong tiêu chuẩn kỹ thuật.
*
* Cuối cùng, hai hàm không chuẩn `decreaseAllowance` và `increaseAllowance`
* đã được thêm vào để giảm thiểu các vấn đề phổ biến liên quan đến việc
* thiết lập ủy quyền. Xem `IERC20.approve`.
*/
contract MyERC20 is IERC20 {
using SafeMath for uint256;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
// NOTE Start of https://github.com/OpenZeppelin/openzeppelin-solidity/blob/v2.3.0/contracts/token/ERC20/ERC20Detailed.sol
string private _name;
string private _symbol;
uint8 private _decimals;
constructor (string memory name, string memory symbol, uint8 decimals) public {
_name = name;
_symbol = symbol;
_decimals = decimals;
_mint(msg.sender, 100000 * 10 ** uint256(decimals)); // CAUTION!
}
/**
* @dev Trả về tên của token.
*/
function name() public view returns (string memory) {
return _name;
}
/**
* @dev Trả về ký hiệu của token, thường là phiên bản ngắn gọn của
* tên.
*/
function symbol() public view returns (string memory) {
return _symbol;
}
/**
* @dev Trả về số chữ số thập phân được sử dụng để hiển thị cho người dùng.
* Ví dụ, nếu `decimals` là `2`, số dư của token `505` sẽ
* được hiển thị cho người dùng dưới dạng `5,05` (`505 / 10 ** 2`).
*
* Các token thường chọn giá trị 18, tương tự mối quan hệ giữa
* Ether và Wei.
*
* > Lưu ý rằng thông tin này chỉ được sử dụng cho _mục đích_ hiển thị: nó
* không ảnh hưởng đến bất kỳ phép tính nào của hợp đồng, bao gồm
* `IERC20.balanceOf` và `IERC20.transfer`.
*/
function decimals() public view returns (uint8) {
return _decimals;
}
// NOTE End of https://github.com/OpenZeppelin/openzeppelin-solidity/blob/v2.3.0/contracts/token/ERC20/ERC20Detailed.sol
uint256 private _totalSupply;
/**
* @dev Xem `IERC20.totalSupply`.
*/
function totalSupply() public view returns (uint256) {
return _totalSupply;
}
/**
* @dev Xem `IERC20.balanceOf`.
*/
function balanceOf(address tài khoản) public view returns (uint256) {
return _balances[tài khoản];
}
/**
* @dev Xem `IERC20.transfer`.
*
* Yêu cầu:
*
* - `recipient` không được là địa chỉ không hợp lệ.
* - người gọi phải có số dư tối thiểu là `amount`.
*/
function transfer(address recipient, uint256 amount) public returns (bool) {
_transfer(msg.sender, recipient, amount);
return true;
}
/**
* @dev Xem `IERC20.allowance`.
*/
function allowance(address owner, address spender) public view returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev Xem `IERC20.approve`.
*
* Yêu cầu:
*
* - `spender` không được là địa chỉ không hợp lệ.
*/
function approve(address spender, uint256 value) public returns (bool) {
_approve(msg.sender, spender, value);
return true;
}
/**
* @dev Xem `IERC20.transferFrom`.
*
* Kích hoạt sự kiện `Approval` để chỉ thị phân quyền cập nhật. Điều này không
* bắt buộc theo EIP. Xem phần lưu ý ở đầu mục ERC20`;
*
* Yêu cầu:
* - `sender` và `recipient` không được là địa chỉ không hợp lệ.
* - `sender` phải có số dư tối thiểu là `value`.
* - Người gọi phải có phân quyền cho token của `sender` ít nhất là
* `amount`.
*/
function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, msg.sender, _allowances[sender][msg.sender].sub(amount));
return true;
}
/**
* @dev Tăng không đáng kể tỷ lệ phân quyền được người gọi cấp cho `spender`.
*
* Đây là một phương pháp thay thế cho `approve` có thể được sử dụng để giảm thiểu
* các vấn đề được mô tả trong `IERC20.approve`.
*
* Kích hoạt sự kiện `Approval` để chỉ thị phân quyền cập nhật.
*
* Yêu cầu:
*
* - `spender` không được là địa chỉ không hợp lệ.
*/
function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
_approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue));
return true;
}
/**
* @dev Tăng tỷ lệ phân quyền được người gọi cấp cho `spender` một cách an toàn.
*
* Đây là một phương pháp thay thế cho `approve` có thể được sử dụng để giảm thiểu
* các vấn đề được mô tả trong `IERC20.approve`.
*
* Kích hoạt sự kiện `Approval` để chỉ thị phân quyền cập nhật.
*
* Yêu cầu:
*
* - `spender` không được là địa chỉ không hợp lệ.
* - `spender` phải có giá trị phân quyền cho người gọi ít nhất là
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
_approve(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue));
return true;
}
/**
* @dev Di chuyển `amount` token từ `sender` sang `recipient`.
*
* Đây là hàm nội bộ tương đương với `transfer` và có thể được dùng để
* ví dụ, triển khai phí tự động cho token, các cơ chế xử phạt, v.v.
*
* Kích hoạt sự kiện `Transfer`.
*
* Yêu cầu:
*
* - `sender` không được là địa chỉ không hợp lệ.
* - `recipient` không được là địa chỉ không hợp lệ.
* - `sender` phải có số dư tối thiểu là `amount`.
*/
function _transfer(address sender, address recipient, uint256 amount) internal {
require(sender != address(0), "ERC20: chuyển từ địa chỉ không hợp lệ");
require(recipient != address(0), "ERC20: chuyển đến địa chỉ không hợp lệ");
_balances[sender] = _balances[sender].sub(amount);
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
/** @dev Tạo `amount` token và gán chúng cho `tài khoản`, tăng
* tổng cung.
*
* Kích hoạt sự kiện `Transfer` với `from` được đặt thành địa chỉ không hợp lệ.
*
* Yêu cầu:
*
* - `to` không được là địa chỉ không hợp lệ.
*/
function _mint(address tài khoản, uint256 amount) internal {
require(tài khoản != address(0), "ERC20: tạo đến địa chỉ không hợp lệ");
_totalSupply = _totalSupply.add(amount);
_balances[tài khoản] = _balances[tài khoản].add(amount);
emit Transfer(address(0), tài khoản, amount);
}
/**
* @dev Hủy `amount` token từ `tài khoản`, giảm
* tổng cung.
*
* Kích hoạt sự kiện `Transfer` với `to` được thiết lập thành địa chỉ không hợp lệ.
*
* Yêu cầu:
*
* - `tài khoản` không được là địa chỉ không hợp lệ.
* - `tài khoản` phải có ít nhất `amount` token.
*/
function _burn(address tài khoản, uint256 value) internal {
require(tài khoản != address(0), "ERC20: đốt từ địa chỉ không hợp lệ");
_balances[tài khoản] = _balances[tài khoản].sub(value);
_totalSupply = _totalSupply.sub(value);
emit Transfer(tài khoản, address(0), value);
}
/**
* @dev Thiết lập `amount` làm giá trị phân quyền của `spender` cho token của `owner`.
*
* Đây là hàm nội bộ tương đương với `approve` và có thể được dùng để
* ví dụ, thiết lập cơ chế phân quyền tự động cho các hệ thống phụ nhất định, v.v.
*
* Kích hoạt sự kiện `Approval`.
*
* Yêu cầu:
*
* - `owner` không được là địa chỉ không hợp lệ.
* - `spender` không được là địa chỉ không hợp lệ.
*/
function _approve(address owner, address spender, uint256 value) internal {
require(owner != address(0), "ERC20: duyệt đến địa chỉ không hợp lệ");
require(spender != address(0), "ERC20: duyệt đến địa chỉ không hợp lệ");
_allowances[owner][spender] = value;
emit Approval(owner, spender, value);
}
/**
* @dev Hủy `amount` token từ `tài khoản`.`amount` sau đó được trừ khỏi
* giá trị phân quyền của người gọi.
*
* Xem `_burn` và `_approve`.
*/
function _burnFrom(address tài khoản, uint256 amount) internal {
_burn(tài khoản, amount);
_approve(tài khoản, msg.sender, _allowances[tài khoản][msg.sender].sub(amount));
}
}1.2 Tìm hiểu một số phương pháp quan trọng
(1) function balanceOf(address tài khoản) external view returns (uint256);
function balanceOf(address tài khoản) external view returns (uint256);(2) function transfer(address recipient, uint256 amount) external returns (bool);
function transfer(address recipient, uint256 amount) external returns (bool);(3) function approve(address spender, uint256 amount) external returns (bool);
function approve(address spender, uint256 amount) external returns (bool);(4) function _mint(address tài khoản, uint256 amount) internal
function _mint(address tài khoản, uint256 amount) internalLast updated