在区块链技术的世界中,智能合约扮演着至关重要的角色。而ERC(Ethereum Request for Comments)标准则是智能合约开发中不可或缺的一部分。ERC接口为开发者提供了一套规范,使得智能合约在不同平台之间能够相互兼容。本文将带你轻松上手ERC接口,掌握区块链开发必备技能。
一、什么是ERC接口?
ERC接口是一系列由以太坊社区提出的规范,旨在为智能合约提供一个标准化的开发框架。这些规范包括ERC-20、ERC-721、ERC-1155等,它们定义了智能合约的接口、功能和行为。
二、ERC-20:通用的代币标准
ERC-20是最为常见的智能合约标准之一,它定义了代币的基本功能,如余额查询、转账、代币供应量等。以下是一个简单的ERC-20代币合约示例:
pragma solidity ^0.8.0;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) 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);
}
contract ERC20 is IERC20 {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
constructor(uint256 initialSupply) {
_totalSupply = initialSupply;
_balances[msg.sender] = initialSupply;
}
function totalSupply() public view override returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) public view override returns (uint256) {
return _balances[account];
}
function transfer(address recipient, uint256 amount) public override returns (bool) {
_transfer(msg.sender, recipient, amount);
return true;
}
function approve(address spender, uint256 amount) public override returns (bool) {
_allowances[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function allowance(address owner, address spender) public view override returns (uint256) {
return _allowances[owner][spender];
}
function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, msg.sender, _allowances[sender][msg.sender] - amount);
return true;
}
function _transfer(address sender, address recipient, uint256 amount) internal {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(sender, recipient, amount);
_balances[sender] -= amount;
_balances[recipient] += amount;
emit Transfer(sender, recipient, amount);
}
function _approve(address owner, address spender, uint256 amount) internal {
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);
}
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}
三、ERC-721:非同质化代币标准
ERC-721定义了非同质化代币(NFT)的基本功能,如创建、转移、查询代币拥有者等。以下是一个简单的ERC-721代币合约示例:
pragma solidity ^0.8.0;
interface IERC721 {
function totalSupply() external view returns (uint256);
function balanceOf(address owner) external view returns (uint256);
function ownerOf(uint256 tokenId) external view returns (address);
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function transferFrom(address from, address to, uint256 tokenId) external;
function approve(address to, uint256 tokenId) external;
function getApproved(uint256 tokenId) external view returns (address);
function setApprovalForAll(address operator, bool approved) external;
function isApprovedForAll(address owner, address operator) external view returns (bool);
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
}
contract ERC721 is IERC721 {
mapping(uint256 => address) private _owners;
mapping(address => uint256) private _balances;
mapping(uint256 => address) private _tokenApprovals;
mapping(address => mapping(address => bool)) private _operatorApprovals;
uint256 private _totalSupply;
constructor() {
_totalSupply = 0;
}
function totalSupply() public view override returns (uint256) {
return _totalSupply;
}
function balanceOf(address owner) public view override returns (uint256) {
require(owner != address(0), "ERC721: balance query for the zero address");
return _balances[owner];
}
function ownerOf(uint256 tokenId) public view override returns (address) {
address owner = _owners[tokenId];
require(owner != address(0), "ERC721: owner query for non-existent token");
return owner;
}
function safeTransferFrom(address from, address to, uint256 tokenId) public override {
transferFrom(from, to, tokenId);
}
function transferFrom(address from, address to, uint256 tokenId) public override {
require(_isOwner(from, tokenId), "ERC721: transfer of token that is not ownable");
_transfer(from, to, tokenId);
}
function approve(address to, uint256 tokenId) public override {
address owner = _owners[tokenId];
require(to != owner, "ERC721: approval to current owner");
_approve(to, tokenId);
}
function getApproved(uint256 tokenId) public view override returns (address) {
return _tokenApprovals[tokenId];
}
function setApprovalForAll(address operator, bool approved) public override {
require(operator != address(0), "ERC721: approve to the zero address");
_operatorApprovals[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function isApprovedForAll(address owner, address operator) public view override returns (bool) {
return _operatorApprovals[owner][operator];
}
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) public override {
safeTransferFrom(from, to, tokenId);
}
function _transfer(address from, address to, uint256 tokenId) internal {
require(from == msg.sender || isApprovedForAll(from, msg.sender), "ERC721: transfer caller is not owner nor approved");
require(to != address(0), "ERC721: transfer to the zero address");
_beforeTokenTransfer(from, to, tokenId);
_balances[from] -= 1;
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(from, to, tokenId);
}
function _approve(address to, uint256 tokenId) internal {
_tokenApprovals[tokenId] = to;
emit Approval(msg.sender, to, tokenId);
}
function _isOwner(address owner, uint256 tokenId) internal view returns (bool) {
return owner == _owners[tokenId];
}
function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual {}
}
四、ERC-1155:多代币标准
ERC-1155结合了ERC-20和ERC-721的特性,允许一个合约同时发行和管理多种类型的代币。以下是一个简单的ERC-1155代币合约示例:
pragma solidity ^0.8.0;
interface IERC1155 {
function balanceOf(address account, uint256 id) external view returns (uint256);
function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory);
function transferFrom(address sender, address recipient, uint256 id, uint256 amount, bytes calldata data) external;
function uri(uint256 id) external view returns (string memory);
function totalSupply(uint256 id) external view returns (uint256);
function isOperatorFor(address operator, address tokenHolder) external view returns (bool);
function safeBatchTransferFrom(address sender, address recipient, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external;
}
contract ERC1155 is IERC1155 {
mapping(address => mapping(uint256 => uint256)) private _balances;
mapping(address => mapping(uint256 => uint256)) private _tokenSupply;
mapping(address => mapping(address => bool)) private _operatorApprovals;
function balanceOf(address account, uint256 id) public view override returns (uint256) {
require(account != address(0), "ERC1155: balance query for the zero address");
return _balances[account][id];
}
function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) public view override returns (uint256[] memory) {
require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");
uint256[] memory batchBalances = new uint256[](accounts.length);
for (uint256 i = 0; i < accounts.length; i++) {
batchBalances[i] = balanceOf(accounts[i], ids[i]);
}
return batchBalances;
}
function transferFrom(address sender, address recipient, uint256 id, uint256 amount, bytes calldata data) public override {
require(sender != address(0), "ERC1155: transfer from the zero address");
require(recipient != address(0), "ERC1155: transfer to the zero address");
require(amount > 0, "ERC1155: transfer amount must be greater than 0");
_transfer(sender, recipient, id, amount);
emit TransferSingle(sender, recipient, id, amount, data);
}
function uri(uint256 id) public view override returns (string memory) {
// Return the token's metadata URI
}
function totalSupply(uint256 id) public view override returns (uint256) {
require(id > 0, "ERC1155: total supply for non-existent token");
return _tokenSupply[id];
}
function isOperatorFor(address operator, address tokenHolder) public view override returns (bool) {
return _operatorApprovals[tokenHolder][operator];
}
function safeBatchTransferFrom(address sender, address recipient, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) public override {
require(sender != address(0), "ERC1155: transfer from the zero address");
require(recipient != address(0), "ERC1155: transfer to the zero address");
for (uint256 i = 0; i < ids.length; i++) {
require(amounts[i] > 0, "ERC1155: transfer amount must be greater than 0");
_transfer(sender, recipient, ids[i], amounts[i]);
}
emit TransferBatch(sender, recipient, ids, amounts, data);
}
function _transfer(address from, address to, uint256 id, uint256 amount) internal {
require(from == msg.sender || isOperatorFor(msg.sender, from), "ERC1155: transfer caller is not owner nor approved");
require(to != address(0), "ERC1155: transfer to the zero address");
_beforeTokenTransfer(from, to, id, amount);
_balances[from][id] -= amount;
_balances[to][id] += amount;
_tokenSupply[id] += amount;
emit TransferSingle(from, to, id, amount, data);
}
function _beforeTokenTransfer(address from, address to, uint256 id, uint256 amount) internal virtual {}
}
五、总结
掌握ERC接口是区块链开发必备技能之一。通过本文的学习,相信你已经对ERC接口有了初步的了解。在实际开发过程中,可以根据项目需求选择合适的ERC标准,并参考上述示例进行开发。祝你在区块链领域取得丰硕的成果!
