在区块链和加密货币的世界里,创建一种属于自己的代币似乎是一件遥不可及的、只有专业开发者才能完成的高难度任务,但实际上,借助以太坊智能平台和Solidity编程语言,任何人都可以通过编写几行代码来发行一个功能完备的代币,这个过程不仅是学习区块链开发的绝佳实践,也是许多去中心化应用和社区治理项目的基础。
本文将带你一步步走过创建以太坊代币的全过程,并提供一份可以直接部署和使用的完整代码,我们将以最流行的ERC-20代币标准为例,这是以太坊上所有 fungible(可互换)代币的黄金标准,包括USDT、LINK等知名代币都遵循此标准。
第一步:准备工作——环境搭建
在开始编写代码之前,你需要准备好以下工具:
- 代码编辑器:如 VS Code,并安装 Solidity 插件,它提供语法高亮和自动补全功能,能极大提升编码体验。
- 以太坊钱包:如 MetaMask,你需要一个钱包来与以太坊网络交互,以及支付部署智能合约时所需的 gas 费。
- 开发环境:我们使用 Remix IDE,这是一个基于网页的集成开发环境,无需在本地安装任何软件,非常适合初学者,你只需在浏览器中访问
remix.ethereum.org即可。
第二步:深入理解ERC-20标准
ERC-20标准定义了一套接口,即一套必须实现的方法和事件,一个完全符合ERC-20标准的代币,必须包含以下核心元素:
-
状态变量:
name: 代币名称,如 "My Awesome Token"。symbol: 代币符号,如 "MAT",通常为2-3个字母。decimals: 代币精度,通常为18,表示代币可以分割到小数点后18位。totalSupply: 代币总供应量。balances: 一个映射,记录每个地址拥有的代币数量。allowances: 一个映射,记录一个地址被授权从另一个地址转移多少代币(用于交易所或第三方合约)。
-
事件:
Transfer(address from, address to, uint256 value): 当代币被转移时触发。Approval(address owner, address spender, uint256 value): 当授权额度被设置时触发。
-
函数:
transfer(address to, uint256 amount): 向指定地址转移代币。approve(address spender, uint256 amount): 授权一个地址可以动用你的代币。transferFrom(address from, address to, uint256 amount): 从被授权的地址转移代币(通常由交易所调用)。balanceOf(address account): 查询指定地址的代币余额。allowance(address owner, address spender): 查询授权额度。
第三步:编写完整的ERC-20代币智能合约
下面是一份完整、可直接使用的ERC-20代币智能合约代码,它包含了所有必要的功能,并添加了一些注释以帮助你理解每一部分的作用。
在Remix IDE中,创建一个新文件(MyToken.sol),然后将以下代码完整复制进去。
// SPDX-License-Identifier: MIT
// 这是一个标准的许可证标识符,告诉别人你的代码是在MIT许可下发布的。
pragma solidity ^0.8.20;
/**MyToken
* @dev 这是一个标准的 ERC-20 代币合约,名为 "My Awesome Token",符号为 "MAT"。
* 它实现了所有必需的接口功能,包括代币的铸造(创建初始供应量)和转移。
*/
contract MyToken {
// --- 状态变量 ---
string public name; // 代币全称
string public symbol; // 代币符号
uint8 public decimals; // 代币精度,18是标准
uint256 public totalSupply; // 代币总供应量
// balances[address] = 该地址拥有的代币数量
mapping(address => uint256) public balances;
// allowances[owner][spender] = owner授权给spender的代币数量
mapping(address => mapping(address => uint256)) public allowances;
// --- 事件 ---
// 当代币被成功转移时触发
event Transfer(address indexed from, add
ress indexed to, uint256 value);
// 当授权额度被设置或修改时触发
event Approval(address indexed owner, address indexed spender, uint256 value);
// --- 构造函数 ---
/**
* @dev 合约部署时执行的函数,用于初始化代币的基本信息。
* 我们创建一个初始供应量,并将其发送给合约的部署者(msg.sender)。
* @param _name 代币名称
* @param _symbol 代币符号
* @param _initialSupply 初始供应量(注意:这是基础单位的数量,如果精度是18,则1000代表 1000 * 10^18)
*/
constructor(string memory _name, string memory _symbol, uint256 _initialSupply) {
name = _name;
symbol = _symbol;
decimals = 18;
totalSupply = _initialSupply * (10 ** uint256(decimals)); // 根据精度调整总供应量
balances[msg.sender] = totalSupply; // 将所有代币分配给合约部署者
emit Transfer(address(0), msg.sender, totalSupply); // 触发Transfer事件,从0地址(创世)转移到部署者
}
// --- ERC-20 标准函数 ---
/**
* @dev 获取一个地址的代币余额
* @param account 要查询的地址
* @return 该地址拥有的代币数量
*/
function balanceOf(address account) public view returns (uint256) {
return balances[account];
}
/**
* @dev �授权一个地址可以动用你的代币
* @param spender 被授权的地址
* @param amount 授权的数量
* @return 成功时返回true
*/
function approve(address spender, uint256 amount) public returns (bool) {
_approve(msg.sender, spender, amount);
return true;
}
/**
* @dev 从你的账户向另一个地址转移代币
* @param to 接收代币的地址
* @param amount 转移的数量
* @return 成功时返回true
*/
function transfer(address to, uint256 amount) public returns (bool) {
_transfer(msg.sender, to, amount);
return true;
}
/**
* @dev 从被授权的地址转移代币(通常由第三方合约调用)
* @param from 转出代币的地址
* @param to 接收代币的地址
* @param amount 转移的数量
* @return 成功时返回true
*/
function transferFrom(address from, address to, uint256 amount) public returns (bool) {
// 首先检查授权额度是否足够
uint256 currentAllowance = allowances[from][msg.sender];
require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
// 更新授权额度
unchecked {
allowances[from][msg.sender] = currentAllowance - amount;
}
// 执行实际的转移
_transfer(from, to, amount);
return true;
}
/**
* @dev �回一个地址对另一个地址的授权额度
* @param owner 授权方
* @param spender 被授权方
* @return 授权数量
*/
function allowance(address owner, address spender) public view returns (uint256) {
return allowances[owner][spender];
}
// --- 内部函数 (Internal Functions) ---
/**
* @dev 内部函数:执行代币转移的核心逻辑
* @param from 转出地址
* @param to 接收地址
* @param amount 转移数量
*/
function _transfer(address from, address to, uint256 amount) internal {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
require(balances[from] >= amount, "ERC20: transfer amount exceeds balance");
balances[from] -= amount;
balances[to] += amount;
emit Transfer(from, to, amount);
}
/**
* @dev 内部函数:执行授权的核心逻辑
* @param owner 授权方
* @param spender 被授权方
* @param amount 授权数量
*/
function _approve(address owner, address spender, uint256 amount) internal {
require(owner != address(0), "ERC20: approve from the zero