想给自己的项目快速做一套「链上数字积分」?其实并不需要重新开发一条区块链。跟着本文,30 分钟内即可上手部署一枚符合主流标准的 以太坊代币。
从 Token 与 Coin 的区别说起
为什么会有 Token?
- Coin(原生币):如 BTC、ETH,各自拥有独立区块链,为网络提供共识、区块激励与底层价值锚定。
- Token(代币):基于已有区块链发行的合约资产,可代表积分、会员等级、虚拟装备,甚至金融衍生品。
| 特性 | Coin | Token |
|---|---|---|
| 独立区块链 | 需要 | 不需要 |
| 开发门槛 | 高,需要共识层设计 | 低,只需写好智能合约 |
| 生态整合 | 自成体系 | 直接借助主链工具、钱包、交易所 |
| 发行成本 | 高 | 低至一次浏览器交易手续费 |
一句话:用 代币 就能把一个精巧的运营创意直接「空投」到全球数千万钱包,而不必再造一条链。
👉 想在测试网零 Gas 费练习?立刻体验去中心化交易所基本交互流程。
必知概念:ERC 标准与家族
在以太坊发行代币,实际就是把一段符合 ERC(Ethereum Request for Comment) 标准的智能合约部署到链上。常用版本:
- ERC20:可互换代币 —— DeFi、ICO 最常用的「万能插头」。
- ERC721:不可互换代币 NFT —— 艺术品、游戏道具。
- ERC1155:一合约多 Token 类型 —— 游戏装备批量管理。
- ERC777:向下兼容 ERC20,新增「钩子」功能,防止误转。
- ERC223 / ERC827:增强转账安全性或授权模型。
一次写对 ERC20,足以覆盖 90% 的代币场景。
ERC20 核心接口一次搞懂
只需要实现以下 6 个函数 与 2 个事件,各钱包、DApp 就能自动理解你的代币。
必备函数
totalSupply()返回发行总量balanceOf(address _owner)查询任意地址余额transfer(address _to, uint256 _value)用户直接转账transferFrom代理转账(第三方平台帮用户操作)approve授权额度allowance查询剩余授权额度
关键事件
Transfer:转账日志Approval:授权日志
只要合约语法正确、接口完整,即可在 MetaMask、Trust Wallet 等常见钱包里正常显示余额和 Logo。
30 分钟实战:创建「DemoToken(DMT)」
以下演示用浏览器版 Remix IDE,连 Metamask 一步上链,全部免费在 Ropsten 测试网验证。
1. 准备 3 个 Solidity 文件
ERC20 接口(保存为 IERC20.sol)
pragma solidity ^0.4.25;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address who) external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function transferFrom(address from, address to, uint256 value) external returns (bool);
function approve(address spender, uint256 value) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
安全运算库(保存为 SafeMath.sol)
pragma solidity ^0.4.25;
library SafeMath {
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) return 0;
uint256 c = a * b;
assert(c / a == b);
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return a / b;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
主代币合约(保存为 DemoToken.sol)
pragma solidity ^0.4.25;
import "./IERC20.sol";
import "./SafeMath.sol";
contract DemoToken is IERC20 {
using SafeMath for uint256;
string public _name;
string public _symbol;
uint8 public _decimals;
uint256 public _totalSupply;
mapping(address => uint256) balances;
mapping(address => mapping(address => uint256)) allowed;
constructor(string name, string symbol, uint8 decimals, uint256 totalSupply) public {
_name = name;
_symbol = symbol;
_decimals = decimals;
_totalSupply = totalSupply * (10 ** uint256(decimals));
balances[msg.sender] = _totalSupply;
}
function name() public view returns (string) { return _name; }
function symbol() public view returns (string) { return _symbol; }
function decimals() public view returns (uint8) { return _decimals; }
function totalSupply() public view returns (uint256) { return _totalSupply; }
function transfer(address _to, uint256 _value) public returns (bool) {
require(_to != address(0));
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
emit Transfer(msg.sender, _to, _value);
return true;
}
function balanceOf(address _owner) public view returns (uint256 balance) {
return balances[_owner];
}
function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
require(_to != address(0));
balances[_from] = balances[_from].sub(_value);
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
emit Transfer(_from, _to, _value);
return true;
}
function approve(address _spender, uint256 _value) public returns (bool) {
allowed[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
function allowance(address _owner, address _spender) public view returns (uint256) {
return allowed[_owner][_spender];
}
}
2. Remix 一键编译 & 部署
- 在 Remix 左侧「Solidity Compiler」选 0.4.25 版本,点击 Compile(GTK 需全部绿色✅)。
- 切换「Deploy & Run」:
- Environment:用 Injected Web3(直接连 MetaMask)。
- Contract 选
DemoToken - DemoToken.sol。 - Constructor 参数随便填:
- name:
DemoToken - symbol:
DMT - decimals:
18 - totalSupply:
1000000
- name:
- 点击 Transact,MetaMask 弹窗确认,Ropsten 测试网 Gas 全部由水龙头提供。
- 待区块确认后,Terminal 输出合约地址
0x...。
3. 钱包与浏览器验证
- MetaMask:在资产页点击「添加代币」→ 用合约地址自动抓取名称 & 符号。
- Etherscan 测试网:粘贴合约地址,在 Token Tracker 标签中即可看到
DMT的转账历史。
恭喜!你已拥有一枚 100% 功能齐全的以太坊代币,支持钱包转账、DEX 上架、DeFi 质押等一切基础操作。
👉 想亲手挂单交易刚创建的新代币?了解自动化做市商必备设置。
如何在项目中落地?
三种经典场景
- 社区积分:完成一次任务即空投 1 DMT,积分可折抵未来订阅费。
- 预售通证:发币后上架 Uniswap 创建流动性,用户以太币即可购得。
- 链上门票:每个 NFT 对应一张演唱会票,ERC20 则用于买爆米花。
下一步进阶
- 给合约加上 白名单、黑名单、可升级代理。
- 用 OpenZeppelin Wizard 一键生成带销毁、铸币角色控制的新版 ERC20。
- 集成 Snapshot 投票治理,让代币成为 DAO 治理票权。
常见问题 FAQ
Q1:发代币需要「备案」吗?
A:仅链上部署无需许可,但若涉及募资、交易推广,请遵守所在地金融法规。
Q2:能把 Logo & 简介显示在所有钱包里吗?
A:上传 Token 信息到 GitHub + PR 到 Trust Wallet Assets 库,或通过 Ethhub、CoinGecko API 提交。
Q3:转账收不到代币?
A:多半是合约未触发 Transfer 事件,或目标地址输入错误。务必先用测试网演练。
Q4:Gas 费太贵怎么办?
A:选择 Polygon、Arbitrum、Optimism 等 Layer2 发行;手续费低至几美分。
Q5:如何限制随意增发?
A:把铸币 mint() 函数加上 onlyOwner 修饰符,仅合约部署者可控,后续锁定权限即可完成总量通缩。
Q6:ERC20 还能兼容 NFT 吗?
A:不可互换。需再部署 ERC721/1155。但根据业务可以为 ERC721 持有者额外空投 ERC20「功勋点」进行结合。
一行总结
“只要把创意写成一句 Solidity,就能让全球几千万钱包成为你的用户。”
祝你玩得开心,下一篇将示范如何给新代币创建无需许可的去中心化交易对,敬请期待!