# 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界面调试合约函数：

![图片](https://3485192048-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2Fhypergraph-decentralized-data-engine%2F-M_C7GPthYVM6xaV8PFO%2F-M_C7JaE_ZB_YOHBOvEe%2F0.png?generation=1620496699345479\&alt=media)

下面重点说一下用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>
