# ERC720 开发实例

***本教程是技术大牛 luofei614 的DApp开发学习笔记，编成教程分享给开发者。***

**ERC20代币**

ERC20是以太坊流行的代币协议，ERC20名称的由来是：以太坊有个需求提案的地方叫ERC，GitHub自动分配20的编号，所以大家习惯叫ERC20

协议结构：

// ----------------------------------------------------------------------------

// ERC Token Standard #20 Interface

// <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md>

// ----------------------------------------------------------------------------

contract ERC20Interface {

&#x20;//总发行量

&#x20;function totalSupply() public constant returns (uint);

&#x20;//账户余额

&#x20;function balanceOf(address tokenOwner) public constant returns (uint balance);

&#x20;//配额带支付， 一般是设置合约地址待支付的权限

&#x20;function allowance(address tokenOwner, address spender) public constant returns (uint remaining);

&#x20;//转账

&#x20;function transfer(address to, uint tokens) public returns (bool success);

&#x20;//审批， 设置合约地址的allowance配额数量

&#x20;function approve(address spender, uint tokens) public returns (bool success);

&#x20;//从其他账户转账，通常与approve一起用

&#x20;function transferFrom(address from, address to, uint tokens) public returns (bool success);

&#x20;//转账事件

&#x20;event Transfer(address indexed from, address indexed to, uint tokens);

&#x20;//审批事件

&#x20;event Approval(address indexed tokenOwner, address indexed spender, uint tokens);

}

合约中实际是有两个mapping来记录余额和配合的

mapping(adress => uint256) balances; //记录地址有多少代币余额

mapping(address => mapping(address => unit256)) public allowed;//记录配额

转账有两个方法

* transfer， 是当前用户（msg.sender） 转账给指定地址， 改变的是balances的状态结构。
* transferFrom，是from参数地址向to参数地址转账， from不一定是msg.sender ，所以需要from地址向授权给合约地址配额， 合约地址才能实现转账。 设置配额改变的是allowed状态，这个是个二级结构，第一级的address是对应了转出账号地址（from） ， 第二级的address对应的是智能合约地址，被授权的智能合约才能转账，等会儿会做个水龙头的合约作为演示。

实现合约可以借助 openzeppelin ， 它已经封装好了父类， 子类继承它即可

新建一个项目：

truffle init

安装依赖：

yarn add  @openzeppelin/contracts

创建合约： contracts/MTK.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MTK is ERC20 {

&#x20;//发行一个币的名称叫MyToken， 简称MTK

&#x20;constructor() ERC20("MyToken", "MTK") {

&#x20;\_mint(msg.sender, 1000);//发现总量,TODO还没有找到怎么设置小数位数

&#x20;}

}

先用remix调试， 这时候因为import导入多文件， 不方便把代码复制到remix上调试。 所以要建立remix的本地服务，让remix调试本地文件

安装remixd

npm install -g @remix-project/remixd

在项目根目录下启动服务：

sudo remixd -s \`pwd\` --remix-ide <https://remix.ethereum.org>

此时在remix文件的wordspaces选择为localhost即可。

编译和部署好合约后，可以用remix界面调试合约函数：

![图片](/files/-M_C7JaE_ZB_YOHBOvEe)

下面重点说一下用truflle console 命令行调试合约。

在migrate文件夹下建立2\_MTK.js文件：

const MTK = artifacts.require("MTK");

module.exports = function (deployer) {

&#x20; deployer.deploy(MTK);

};

编辑truffle-config.js 修改编译器版本为 0.8.4

编译：truffle compile , 并部署合约 truffle migrate

进入命令行：

truflle console

\>MTK.address //获得部署合约地址

\>let instance=await MTK.deployed() //获得合约实例

\>let accounts = await web3.eth.getAccounts() //获得账户

\>instance.totalSupply() //获得总发行量

\>instance.balanceOf(accounts\[0]) //获得账户余额

\>instance.transfer(accounts\[1],web3.utils.toBN(100)) //向第二个账户打100MTK

\>instance.balanceOf(accounts\[0]) //获得第二个账户余额

下面在说下建立MTK的水龙头合约，演示approve和transferFrom的使用。

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./MTK.sol";

contract MTKFaucet {

&#x20;   MTK public MTKInstance;

&#x20;   address public MTKOwner;

&#x20;   constructor(MTK \_MTKInstance, address \_MTKOwner)  {

&#x20;       MTKInstance=\_MTKInstance;

&#x20;       MTKOwner=\_MTKOwner;

&#x20;   }

&#x20;   function (uint withdraw\_amount) public{

&#x20;       require(withdraw\_amount<=1000);

&#x20;       MTKInstance.transferFrom(MTKOwner,msg.sender,withdraw\_amount);

&#x20;   }

}

创建上面合约时需要传递已经部署好的MTK合约地址和部署账户的地址。

建立migrate/3\_MTKFaucet.js文件

const MTKFaucet = artifacts.require("MTKFaucet");

const MTK = artifacts.require("MTK");//前一步MTK已经部署好，这里可以用MTK.address获得部署地址

module.exports = function (deployer,network,accounts) {

&#x20; deployer.deploy(MTKFaucet,MTK.address,accounts\[0]);

};

调试MTKFaucet合约：

truffle console

\>let instance=await MTKFaucet.deployed()

\>let accounts=await web3.eth.getAccounts();

\>instance.withdraw(1,{from:accounts\[1]}) //这会报错 revert ERC20: transfer amount exceeds allowance ， 是因为没有分配限额

\>let MTKInstance=await MTK.deployed()

\>MTKInstance.approve(MTKFaucet.address,10) //对合约分配限额为10

\>instance.withdraw(1,{from:accounts\[1]}) //这时候管用了

\>MTKFaucetInstances.withdraw(11,{from:accounts\[1]}) //超过限额还会报错

代币的转账和以太坊的转账是有区别的， 太坊的转账是改变的目标地址的状态。 而代币的转账是改变的合约的状态。

大多数交易所的账号地址是合约地址， 要判断交易所合约是否支持代币，如果向不支持代币的合约转账代币， 这个代币将永远卡住取不出来。

PS: ERC721就是NFT的协议。<https://docs.openzeppelin.com/contracts/4.x/erc721>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.hg.network/dapp-kai-fa-jiao-cheng/erc720-kai-fa-shi-li.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
