Web3学习之 ERC20

Web3学习之ERC20ERC20ERC20是以太坊上的一种代币标准,它定义了一组接口(方法和事件),使得代币可以在不同的应用程序、钱包和交易所之间进行互操作。ERC20标准使得创建和使用代币变得简单和一致,是最广泛采用的代币标准之一。EIP/ERC标准标准:降低沟通协作成本

Web3学习之 ERC20

ERC20

ERC20 是以太坊上的一种代币标准,它定义了一组接口(方法和事件),使得代币可以在不同的应用程序、钱包和交易所之间进行互操作。ERC20 标准使得创建和使用代币变得简单和一致,是最广泛采用的代币标准之一。

image-20240708151046000.png

EIP / ERC 标准

EIP(Ethereum Improvement Proposal,以太坊改进提案)是以太坊社区提出和讨论对以太坊平台进行改进和变更的正式流程。EIP 分为几个主要分类,每个分类有不同的目的和用途。以下是 EIP 的主要分类:

  1. 核心(Core)

    • 这些是影响以太坊核心协议的改进提案,包括共识协议更改、网络升级和硬分叉。
    • Core EIP 是对以太坊的底层协议和运行机制的改进,需要进行广泛的测试和社区共识。
  2. 接口(Interface,ERC)

    • 这些提案用于标准化以太坊上的应用程序接口(API)和智能合约标准。
    • 最著名的例子是 ERC-20 标准,它定义了代币的接口,使代币在不同的应用程序和交易所之间具有互操作性。
  3. 网络(Networking)

    • 涉及以太坊网络协议的改进提案,包括对节点间通信的更改。
    • Networking EIP 包括对以太坊网络消息传递协议和数据传输的改进。
  4. 元(Meta)

    • Meta EIP 涉及以太坊改进提案流程本身的改进,包括对 EIP 模板、格式和流程的更改。
    • 这些提案不直接涉及以太坊代码或协议的更改,而是关于管理和协调改进提案的流程。
  5. 信息(Informational)

    • 提供与以太坊设计和使用相关的指导和信息。
    • 这些提案不提议任何新的功能或标准,只是为开发者和用户提供信息和最佳实践。

示例

  • EIP-1:描述了 EIP 的流程和指南,属于 Meta 分类。
  • EIP-20(ERC-20):定义了标准的代币接口,属于 Interface 分类。
  • EIP-1559:对以太坊的交易费用结构进行改进,属于 Core 分类。

ERC20 标准提供了一组基础接口,使得代币可以在以太坊生态系统中方便地创建、管理和交换。

区分原生币(Coin)和代币(Token)

你所提到的区别是区分原生币(Coin)和代币(Token)的关键概念。以下是更详细的解释:

以太币(Coin)

  • 原生币: 以太币(Ether,ETH)是以太坊区块链的原生加密货币。它直接由区块链协议生成和管理。
  • 用途: 主要用于支付网络上的交易费用(Gas)以及奖励矿工(现在是验证者)。
  • 特性: 原生币的交易是直接在区块链上进行的,不需要任何智能合约。
  • 地址: 以太币的交易地址是由以太坊协议生成的。

ERC20 代币(Token)

  • 智能合约币: ERC20 代币是通过智能合约创建和管理的加密货币。它们不是区块链的原生币,而是构建在区块链之上的。
  • 用途: 可以代表各种资产或功能,如稳定币、权益证明、治理代币等。
  • 特性: ERC20 代币遵循以太坊改进提案 20(EIP-20)的标准,实现了一组基本的接口和功能,使其能够在去中心化应用(DApps)之间互操作。
  • 地址: ERC20 代币的合约地址是由智能合约生成的。

WETH(Wrapped Ether)

  • 包装以太币: WETH(Wrapped Ether)是将 ETH 包装成 ERC20 代币的形式,使其能够在需要 ERC20 标准的去中心化应用中使用。
  • 用途: 由于 ETH 不是 ERC20 代币,有些 DApps 需要使用 ERC20 标准,因此 WETH 充当桥梁,使 ETH 可以像其他 ERC20 代币一样使用。
  • 特性: 1 WETH 始终等于 1 ETH,用户可以随时在 WETH 和 ETH 之间转换。
  • 合约地址: WETH 有自己的智能合约地址,用户通过该地址进行 WETH 与 ETH 的转换。

代码示例

以下是一个简单的将 ETH 转换为 WETH 的合约示例:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IWETH {
    function deposit() external payable;
    function withdraw(uint256 wad) external;
}

contract ETHToWETH {
    IWETH public weth;

    constructor(address _weth) {
        weth = IWETH(_weth);
    }

    function convertEthToWeth() external payable {
        require(msg.value > 0, "Must send ETH to convert to WETH");
        weth.deposit{value: msg.value}();
    }

    function convertWethToEth(uint256 amount) external {
        require(amount > 0, "Must specify WETH amount to convert to ETH");
        weth.withdraw(amount);
        payable(msg.sender).transfer(amount);
    }

    receive() external payable {
        revert("Send ETH directly to this contract is not allowed");
    }
}

https://eips.ethereum.org/EIPS/eip-20

image-20240707140033040.png

标准接口允许以太坊上的任何代币被其他应用程序重复使用:从钱包到去中心化交易所。

必备技能:不适用任何库的情况下,纯手撸ERC20

不要死记硬背

第一性原理

从区块链的角度出发实现一个数字货币一个链上的数字货币

思考:在技术层面需要什么接口

任何一个货币系统都有一个余额表,知道用户名是谁、余额是多少,在ERC20里面叫 balanceOf

它接收一个地址作为参数,返回这个地址的余额

知道总量是多少,在ERC20里面叫 total supply

用户之间需要转账,在ERC20里面叫 transfer

还有approveallowance 可以用于:

两个普通用户之间的一个约定,也可以用于一个普通用户和一个合约用户之间的一个约定。

例如 A 和 B ,其中A有1万元,但是它不想经常使用,因为没有很好的管理习惯,使用的越多,风险越大。所以A 新建了一个 B 账户,B 账户最多可以使用 A 账户中1万元的100块钱。

这就是 approveallowancetransferFrom 所服务的一个场景

6 + 3 + 2

6 个必备的方法

3个可选的方法

2个必备的事件

思考:获取 USDT 合约的所有地址和地址的持币数量

通过事件获取合约的所有持币地址

https://etherscan.io/token/0xdac17f958d2ee523a2206206994597c13d831ec7#balances

image-20240707141841054.png

ERC20 还有很多插件:mint 、burn ...

https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC20

image-20240707142322006.png

USDT的合约的坑

以太坊 USDT 没有完全遵循 ERC20 标准,它transfer没有返回值

ERC20标准

image-20240707144547522.png

USDT 的实现

image-20240707144850385.png

USDT合约代码

image-20240707144455181.png

参考:

解决:

1 安装USDT 修改你的 interface

2 使用 SafeERC20

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/utils/SafeERC20.sol

实现一个简单的ERC20

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract BaseERC20 {
    string public name;
    string public symbol;
    uint8 public decimals;

    uint256 public totalSupply;

    mapping(address => uint256) balances;

    mapping(address => mapping(address => uint256)) allowances;

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    constructor() {
        // 设置代币的名称、符号、小数位数和总供应量
        name = "BaseERC20";
        symbol = "BERC20";
        decimals = 18;
        totalSupply = 100000000 * (10 ** uint256(decimals));

        // 初始供应量分配给合约部署者
        balances[msg.sender] = totalSupply;
    }

    function balanceOf(address _owner) public view returns (uint256 balance) {
        return balances[_owner];
    }

    function transfer(address _to, uint256 _value) public returns (bool success) {
        require(balances[msg.sender] >= _value && _value > 0, "ERC20: transfer amount exceeds balance");
        require(_to != address(0), "ERC20: transfer to the zero address");

        unchecked {
            balances[msg.sender] -= _value;
        }
        unchecked {
            balances[_to] += _value;
        }

        emit Transfer(msg.sender, _to, _value);
        return true;
    }

    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        require(balances[_from] >= _value && _value > 0, "ERC20: transfer amount exceeds balance");
        require(_to != address(0), "ERC20: transfer to the zero address");
        require(allowances[_from][msg.sender] >= _value, "ERC20: transfer amount exceeds allowance");

        unchecked {
            balances[_from] -= _value;
        }
        unchecked {
            balances[_to] += _value;
        }
        unchecked {
            allowances[_from][msg.sender] -= _value;
        }

        emit Transfer(_from, _to, _value);
        return true;
    }

    function approve(address _spender, uint256 _value) public returns (bool success) {
        require(_spender != address(0), "ERC20: approve to the zero address");
        allowances[msg.sender][_spender] = _value;

        emit Approval(msg.sender, _spender, _value);
        return true;
    }

    function allowance(address _owner, address _spender) public view returns (uint256 remaining) {
        return allowances[_owner][_spender];
    }
}

注意:实际一般不自己实现,使用[OpenZeppelin]<https://www.openzeppelin.com/contracts>

来实现。

OpenZeppelin 是一个开源的区块链开发工具和智能合约库,旨在帮助开发者安全地构建和部署智能合约。它提供了一系列经过审核和验证的智能合约实现,包括代币标准、访问控制、支付通道等,极大地简化了以太坊和其他区块链平台上的开发工作。

OpenZeppelin 的主要功能和特点

  1. 安全性: OpenZeppelin 合约经过广泛的审计和验证,确保代码的安全性和可靠性。
  2. 模块化: 提供可重用的模块化合约,使得开发者可以轻松组合和扩展功能。
  3. 标准化: 实现了广泛接受的标准,如 ERC20、ERC721 和 ERC1155 等。
  4. 文档和支持: 提供详尽的文档和活跃的社区支持,帮助开发者快速上手。

使用 OpenZeppelin 的好处

  • 节省时间: 提供了常用合约的实现,减少了从零开始编写合约的时间。
  • 提高安全性: 经过审核的合约减少了安全漏洞的风险。
  • 社区支持: OpenZeppelin 拥有一个庞大且活跃的开发者社区,可以提供支持和帮助。

OpenZeppelin 是区块链开发中不可或缺的工具,提供了安全、可靠和高效的智能合约实现。通过使用 OpenZeppelin,开发者可以更专注于业务逻辑的实现,而不必担心底层合约的安全和稳定性。

参考

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

0 条评论

请先 登录 后评论
寻月隐君
寻月隐君
0x750E...B6f5
不要放弃,如果你喜欢这件事,就不要放弃。如果你不喜欢,那这也不好,因为一个人不应该做自己不喜欢的事。