> For the complete documentation index, see [llms.txt](https://docs.hg.network/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.hg.network/dapp-kai-fa-jiao-cheng/erc721-dapp-kai-fa-shi-li.md).

# ERC721 DApp 开发实例

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

DAPP例子：领取宠物

* 拉群官方pet-shop的项目模板：

truffle unbox pet-shop

truffle unbox 可以下载一些项目模板代码。 更多查看： <https://www.trufflesuite.com/boxes>

* 目录结构：

contracts/: 存放合约文件夹

migrations/: 部署迁移改变文件夹

test/: 测试文件

truffle-config.js: 配置文件

* 安装node模块依赖

yarn install

* 运行项目

yarn dev

浏览器看见界面如下：

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

此项目只显示了前端， 还没实现合约和交互。 我们继续添加代码让他可以实现区块链领宠物。

原始教程地址：<https://www.trufflesuite.com/tutorial>

* 下面来创建智能合约：

在contracts目录下添加文件Adoption.sol文件：

pragma solidity ^0.5.0;

contract Adoption {

&#x20; address\[16] public adopters;

&#x20; function adopt(uint petId) public returns(uint){

&#x20;      require(petId>=0&\&petId<=15);

&#x20;      adopters\[petId]=msg.sender;

&#x20;      return petId;

&#x20; }

&#x20; function getAdopters() public view returns(address\[16] memory){

&#x20;     return adopters;

&#x20; }

}

编译合约，在项目根目录运行命令

truffle compile

编译的结果是会在build/contracts 生成合约的json文件。 json文件中包含了合约的abi信息和bytecode编码。 这个json中还有一个networks字段，是合约部署完成后会显示出区块链网络内容。

* 创建迁移文件

在migrations目录下创建2\_deploy\_contracts.js文件：

var Adoption = artifacts.require("Adoption");

module.exports = function(deployer) {

&#x20; deployer.deploy(Adoption);

};

* 部署合约

本地打卡Ganache， 默认监听的端口是7545

truffle-config.js 中以及设置了开发环境的测试区块链RPC地址，默认就是为本地测试网络7545的

Ganache，默认有创建十个以太坊账号，并都充了100ETH

部署合约运行命令：

truffle migrate

部署成功后，可以在Ganache界面中看见区块增多，并且第一个账户的以太坊减少， 默认会以第一个账户作为部署账户， 减少的以太坊是部署合约是gas花费。

当 truffle migrate 运行多少时，已经部署过的合约不会再部署，需要重新部署需要在运行时加上参数 --reset

* 编写js代码：

在/src/js/app.js文件中的initWeb3函数中添加如下代码：

初始化Web3，链接钱包：

// Modern dapp browsers...

if (window\.ethereum) {

&#x20;App.web3Provider = window\.ethereum;

&#x20;try {

&#x20;// Request account access

&#x20;await window\.ethereum.enable();

&#x20;} catch (error) {

&#x20;// User denied account access...

&#x20;console.error("User denied account access")

&#x20;}

}

// Legacy dapp browsers...

else if (window\.web3) {

&#x20;App.web3Provider = window\.web3.currentProvider;

}

// If no injected web3 instance is detected, fall back to Ganache

else {

&#x20;App.web3Provider = new Web3.providers.HttpProvider('<http://localhost:7545>');

}

web3 = new Web3(App.web3Provider);

window\.ethereum 是MetaMask插件的对象， window\.ethereum.enable() 可以弹出MetaMask选择钱包。

初始化合约实例，设置Provider：

在initContract方法中添加代码：

&#x20;$.getJSON('Adoption.json', function(data) {

&#x20;     // Get the necessary contract artifact file and instantiate it with @truffle/contract

&#x20;     var AdoptionArtifact = data;

&#x20;     App.contracts.Adoption = TruffleContract(AdoptionArtifact);

&#x20;     // Set the provider for our contract

&#x20;     App.contracts.Adoption.setProvider(App.web3Provider);

&#x20;     // Use our contract to retrieve and mark the adopted pets

&#x20;     return App.markAdopted();

&#x20; });

这个为什么Adoption.json可以直接获得数据， 是因为根目录下bs-config.json中配置了baseDir中有 build/contracts

在markAdopted函数中添加如下代码：

var adoptionInstance;

App.contracts.Adoption.deployed().then(function(instance) {

&#x20; adoptionInstance = instance;

&#x20; return adoptionInstance.getAdopters.call();

}).then(function(adopters) {

&#x20; for (i = 0; i < adopters.length; i++) {

&#x20;   if (adopters\[i] !== '0x0000000000000000000000000000000000000000') {

&#x20;     $('.panel-pet').eq(i).find('button').text('Success').attr('disabled', true);

&#x20;   }

&#x20; }

}).catch(function(err) {

&#x20; console.log(err.message);

});

此代码获取合约中已经被领取的代码，标记为已领取，其他人不能再领取。

handleAdopt 添加如下代码：

var adoptionInstance;

web3.eth.getAccounts(function(error, accounts) {

&#x20;if (error) {

&#x20;console.log(error);

&#x20;}

&#x20;var account = accounts\[0];

&#x20;App.contracts.Adoption.deployed().then(function(instance) {

&#x20;adoptionInstance = instance;

&#x20;// Execute adopt as a transaction by sending account

&#x20;return adoptionInstance.adopt(petId, {from: account});

&#x20;}).then(function(result) {

&#x20;return App.markAdopted();

&#x20;}).catch(function(err) {

&#x20;console.log(err.message);

&#x20;});

});

将上面代码替换注释 replace me

现在这个例子可以完整跑了。 注意在使用时，将自己MetaMask钱包地址切换为测试网络。 并添加一个有余额的账号。 可以Ganache界面上选一个有余额的账号，复制私钥， 在MetaMask钱包创建账号时选择导入私钥。
