小白专享-图解以太坊编程

2025年06月30日更新 19 人订阅
专栏简介 最基础的数据结构 - Merkle 树 MPT树前置 - 前缀树 以太坊的核心数据结构 - MPT树 以太坊数据检索的基石 - 布隆过滤器 入门以太坊的编程的第一步 - Solidity 基本语法 Gas优化的核心 - 以太坊数据存储布局及内存优化 以太坊进阶操作 - 合约调用、地址预测、发送与接收 ETH 以太坊编程进阶 - ABI 编码、函数选择器、合约升级 以太坊代理模式的进阶 - 钻石代理和最小代理 以太坊代理模式的天花板 - 信标代理 以太坊发币 - 超简单发行 ERC-20 代币并上线到 holesky 上 NFT发行 - 超简单发行 NFT 到 holesky 上(包含 ERC165、ERC721Receiver 的含义) 带你手搓一个预言机 - 极简版的 ChainLink VRF 随机数生成 监听合约事件 -- 手把手带你在线、离线部署 The Graph 事件监听 - 合约事件监听的方案汇总 代币集大成者 - 手搓一个ERC1155合约并上线 holesky DeFi 项目的基石 - ERC4626 代币金库协议的实现 智能合约的身份保证 - 数字签名 更安全的签名 - EIP712 结构化签名 ERC20授权的更优方案 - ERC20Permit 签名授权 更安全的钱包 - 最小代码手搓 gnosis safe 多签钱包 空投大杂烩 - 合约实现空投发放的三种方案 发币防止大户提前跑路 - 手搓一个线性释放合约 你也不想你的代币被盗吧? - 手搓实现代币锁和时间锁 Pectra 升级的核心:EIP-7702的原理分析和实操 Resupply 黑客事件分析

以太坊发币 - 超简单发行 ERC-20 代币并上线到 holesky 上

  • shawn_shaw
  • 发布于 2025-04-21 05:12
  • 阅读 1233

ERC20是什么ERC20实际上就是一个用智能合约发行代币的标准。本质上就是用智能合约的代码来规定好这个币的总量有多少,某个地址拥有多少币:https://learnblockchain.cn/shawn_shaw

ERC20是什么

ERC20 实际上就是一个用智能合约发行代币的标准。本质上就是用智能合约的代码来规定好这个币的总量有多少,某个地址拥有多少币。

ERC20 代币的规范

Openzeppelin 中,与 ERC20 代币相关的接口有两个,IERC20IERC20MetadataIERC20 负责代币的行为抽象,IERC20Metadata 负责代币的表现抽象。下面,我们来逐一分析 IERC20IERC20Metadata 的接口。

IERC20Metadata

image.png

  • name():负责返回该代币的名称
  • symbol():负责返回该代币的标志
  • decimal():负责返回该代币的精度

    IERC20

  • 事件
    // 发生转账时,该事件会被释放
    event Transfer(address indexed from, address indexed to, uint256 value);

    // 发生代币授权时,该事件会被触发
    event Approval(address indexed owner, address indexed spender, uint256 value);
  • 函数
    // 返回该代币的总供应量
    function totalSupply() external view returns (uint256);

    // 返回某个地址持有该代币的量
    function balanceOf(address account) external view returns (uint256);

    // 从本调用者地址转到 to 地址
    function transfer(address to, uint256 value) external returns (bool);

    // 返回owner授权给 spender 的代币额度
    function allowance(address owner, address spender) external view returns (uint256);

    // 调用者授权给 spender  amount 单位的代币
    function approve(address spender, uint256 value) external returns (bool);

    // 从 from 地址转出 value 单位的代币到 to 地址,走的是授权机制(需 approve)
    function transferFrom(address from, address to, uint256 value) external returns (bool);

手撸一个 ERC20 代币并上线

使用 foundry 开发、测试、部署一条龙服务 安装 Openzeppelin 的依赖库

forge install OpenZeppelin/openzeppelin-contracts
  1. 编写 ShawnERC20.sol
contract ShawnERC20 is IERC20, IERC20Metadata {
//    记录某个地址的代币数量
    mapping(address => uint256) public override balanceOf;
//    记录某个地址授权给另一个地址的代币数量
    mapping(address => mapping(address => uint256)) public override allowance;
//    总供应量
    uint256 public override  totalSupply;
    string public override name = "Shawn Token";
    string public override symbol = "SC";
    uint8 public override decimals = 10;
    constructor(string memory name_, string memory symbol_){
        name = name_;
        symbol = symbol_;
    }

//    从 msg.sender 转到 to
    function transfer(address to, uint256 value) external returns (bool){
//        msg.sender 扣减
        balanceOf[msg.sender] -= value;
//        to 加上
        balanceOf[to] += value;
        emit Transfer(msg.sender,to,value);
        return true;
    }

//    授权,只做记录,未发生实际转账
    function approve(address spender, uint256 value) external returns (bool){
//        记录 msg.sender 授权给 spender value 数量的代币
        allowance[msg.sender][spender] = value;
        emit Approval(msg.sender,spender, value);
        return true;
    }

//     授权转账
    function transferFrom(address from, address to, uint256 value) external returns (bool){
//        from 从记录中扣减 value 数量的代币
        allowance[from][msg.sender] -= value;
//        实际 from 的余额 扣减
        balanceOf[from] -= value;
//        to 的实际余额增加
        balanceOf[to] += value;
        emit Transfer(from, to, value);
        return true;
    }

//    铸币(当前任何人都能铸币,实际应加上权限控制)
    function mint(uint amount) external {
//        给 sender 加上币
        balanceOf[msg.sender] += amount;
//        供应量增加
        totalSupply += amount;
        emit Transfer(address(0), msg.sender, amount);
    }

//    燃烧
    function burn(uint amount) external {
//        sender 扣减
        balanceOf[msg.sender] -= amount;
//        供应量扣减
        totalSupply -= amount;
        emit Transfer(msg.sender, address(0), amount);
    }
}
  1. 编写测试 ShawnERC20Test.sol(可以不写,直接部署也行)
contract ShawnERC20Test is Test {
    ShawnERC20 token;
    address alice = address(1);
    address bob = address(2);

    function setUp() public {
        token = new ShawnERC20("Shawn Token", "SC");
    }

    function testMint() public {
        vm.prank(alice);
        token.mint(10);
        assertEq(token.totalSupply(), 10);
        assertEq(token.balanceOf(alice), 10);
    }

    function testTransfer() public {
        vm.prank(alice);
        token.mint(10);

        vm.prank(alice);
        token.transfer(bob, 3);

        assertEq(token.balanceOf(alice), 7);
        assertEq(token.balanceOf(bob), 3);
    }

    function testApproveAndTransferFrom() public {
        vm.prank(alice);
        token.mint(10);

        vm.prank(alice);
        token.approve(bob, 3);
        assertEq(token.allowance(alice, bob), 3);

        vm.prank(bob);
        token.transferFrom(alice, bob, 2);

        assertEq(token.balanceOf(bob), 2);
        assertEq(token.balanceOf(alice), 8);
        assertEq(token.allowance(alice, bob), 1);
    }

    function testBurn() public {
        vm.prank(alice);
        token.mint(10);

        vm.prank(alice);
        token.burn(8);

        assertEq(token.balanceOf(alice), 2);
        assertEq(token.totalSupply(), 2);
    }
}
  • 执行测试命令
forge test --match-contract ShawnERC20Test -v

image.png

  1. 编写部署 DeployShawnERC20.sol
contract DeployShawnERC20 is Script {
    function run() external {
        vm.startBroadcast();

        // 部署 ERC20 合约
        ShawnERC20 token = new ShawnERC20("Shawn Token", "SC");

        // mint 给部署者 100 SC
        token.mint(100 ether);

        // 定义其他地址
        address to = address(0xBEEF);         // 接收地址
        address spender = address(0xCAFE);    // 被授权地址

        // 发送 10 SC 给 to
        token.transfer(to, 10 ether);

        // burn 10 SC(从 msg.sender 地址销毁)
        token.burn(10 ether);

        vm.stopBroadcast();
    }
}
  1. 配置环境变量
    • 采用 holesky 测试网络进行合约部署
    • 在项目的根目录下,建立一个.env 文件,配置部署合约的私钥和交易的 rpc 节点
    • 如果你的私钥相应账号上没有 ETH,可以先去水龙头领取免费的 ETH google cloud 水龙头
PRIVATE_KEY=0x_your_private_key
HOLESKY_RPC_URL=https://ethereum-holesky-rpc.publicnode.com
  • foundry.toml 文件中,也进行配置 holesky 网络 rpc 节点
[rpc_endpoints]
holesky = "https://ethereum-holesky-rpc.publicnode.com"
  1. 执行部署命令 在终端中,切换到项目的根目录文件夹下,执行命令
    source .env
forge script script/ERC20/DeployShawnERC20.sol:DeployShawnERC20 \
  --rpc-url $HOLESKY_RPC_URL \
  --private-key $PRIVATE_KEY \
  --broadcast \
  -vvvv
  • 你将会看到

image.png

  • 拿着这个合约地址(0xBc0112bcd06094A18432Fa3610C77c75553c1a0c),我们可以去区块浏览器查看交易记录。 -区块浏览器

image.png

至此,合约已部署上线,后续可通过调用完成 ERC20 代币的授权、转移。

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论