亲手发行你的第一枚以太坊代币:从概念到部署的全程指南

Posted by PDQ881 加密视角 on September 5, 2025

想给自己的项目快速做一套「链上数字积分」?其实并不需要重新开发一条区块链。跟着本文,30 分钟内即可上手部署一枚符合主流标准的 以太坊代币


从 Token 与 Coin 的区别说起

为什么会有 Token?

  • Coin(原生币):如 BTC、ETH,各自拥有独立区块链,为网络提供共识、区块激励与底层价值锚定。
  • Token(代币):基于已有区块链发行的合约资产,可代表积分、会员等级、虚拟装备,甚至金融衍生品。
特性 Coin Token
独立区块链 需要 不需要
开发门槛 高,需要共识层设计 低,只需写好智能合约
生态整合 自成体系 直接借助主链工具、钱包、交易所
发行成本 低至一次浏览器交易手续费

一句话:用 代币 就能把一个精巧的运营创意直接「空投」到全球数千万钱包,而不必再造一条链。

👉 想在测试网零 Gas 费练习?立刻体验去中心化交易所基本交互流程。


必知概念:ERC 标准与家族

在以太坊发行代币,实际就是把一段符合 ERC(Ethereum Request for Comment) 标准的智能合约部署到链上。常用版本:

  1. ERC20:可互换代币 —— DeFi、ICO 最常用的「万能插头」。
  2. ERC721:不可互换代币 NFT —— 艺术品、游戏道具。
  3. ERC1155:一合约多 Token 类型 —— 游戏装备批量管理。
  4. ERC777:向下兼容 ERC20,新增「钩子」功能,防止误转。
  5. 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 一键编译 & 部署

  1. 在 Remix 左侧「Solidity Compiler」选 0.4.25 版本,点击 Compile(GTK 需全部绿色✅)。
  2. 切换「Deploy & Run」:
    • Environment:用 Injected Web3(直接连 MetaMask)。
    • Contract 选 DemoToken - DemoToken.sol
    • Constructor 参数随便填:
      • name:DemoToken
      • symbol:DMT
      • decimals:18
      • totalSupply:1000000
  3. 点击 Transact,MetaMask 弹窗确认,Ropsten 测试网 Gas 全部由水龙头提供。
  4. 待区块确认后,Terminal 输出合约地址 0x...

3. 钱包与浏览器验证

  • MetaMask:在资产页点击「添加代币」→ 用合约地址自动抓取名称 & 符号。
  • Etherscan 测试网:粘贴合约地址,在 Token Tracker 标签中即可看到 DMT 的转账历史。

恭喜!你已拥有一枚 100% 功能齐全的以太坊代币,支持钱包转账、DEX 上架、DeFi 质押等一切基础操作。

👉 想亲手挂单交易刚创建的新代币?了解自动化做市商必备设置。


如何在项目中落地?

三种经典场景

  1. 社区积分:完成一次任务即空投 1 DMT,积分可折抵未来订阅费。
  2. 预售通证:发币后上架 Uniswap 创建流动性,用户以太币即可购得。
  3. 链上门票:每个 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,就能让全球几千万钱包成为你的用户。”

祝你玩得开心,下一篇将示范如何给新代币创建无需许可的去中心化交易对,敬请期待!