前言
本文不会涉及太多理论,目的是教你搭建一个NFTMarketplace以及对应的合约管理系统,话不多说,让我们开始吧。
项目介绍
我们将会搭建一个简易的类似Opensea的nft交易平台,用户可以在上面创建或者购买nft。当用户a创建了一个nft并上架售卖时,这个nft的所有权将会从该用户转到NFTMarketplace这个合约上。当另用户b购买这个nft时,用户b需要向用户a支付该nft的价格,当交易完成时,nft的所有权从合约转到用户b。当这个过程完成时,合约的所有者将收到一笔上架费用,或者可以理解为交易手续费,这笔手续费是由用户a支付的。
当这个过程完成时,你可以去合约管理平台查看交易明细,合约所有者将有权重置上架费用。
项目启动
新建文件夹openseaClone,在文件夹下新建三个文件夹,client,smart_contract,contract_management,依次用于存放客户端代码,智能合约代码,合约管理平台代码
智能合约
cd smart_contract
npm init -y
- 接下来安装项目依赖,
npm i @nomiclabs/hardhat-ethers @nomiclabs/hardhat-waffle @openzeppelin/contracts chai ethereum-waffle ethers hardhat @nomicfoundation/hardhat-toolbox
npx hardhat
这时可以看到hardhat为我们初始化了一些列文件,现在你可以运行npx hardhat test
,如果出现以下图片,则表示示例通过测试
接下来进入contracts文件,删除原有的文件,新建NFTMarketplace.sol
文件,这个就是我们的只能合约代码,在文件中粘贴这个代码,之后打开test文件,删除原有文件,新建NFTMarketplace.js
文件,在文件中粘贴这个代码,之后运行npx hardhat test
,如果出现以下结果则说明测试通过
好了,我们现在准备好合约了,接下来去infura申请一个节点用于与以太坊交互,create new key->network选择Web3 api,输入项目名称->选择ropsten,复制url,并粘贴到下面代码的url后面。
现在,我们将把合约部署到ropsten network,你也可以部署到其他网络,或者你可以先到本地网络进行测试。打开hardhat.config.js
,输入以下代码,你用于发布合约的钱包地址需要有一定的以太币,由于我们是在ropsten network发布合约,你可以去这个地址领取一定数量的币
/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: "0.8.9",
networks: {
ropsten: {
//the url you have created in infura,please place it here
url: '',
//private key of the wallet address that you need to deploy the contract
accounts: ['']
}
}
};
接下来打开scripts/deploy.js,输入以下代码
const hre = require("hardhat");
const fs = require("fs");
async function main () {
const NFTMarketplace = await hre.ethers.getContractFactory("NFTMarketplace")
const nftMarketplace = await NFTMarketplace.deploy()
await nftMarketplace.deployed()
console.log("nftMarketplace deployed to:", nftMarketplace.address)
fs.writeFileSync('./config.js', `export const marketplaceAddress = "${nftMarketplace.address}"`)
}
const runMain = async () => {
try {
await main()
process.exit(0)
} catch (err) {
console.error(err)
process.exit(1)
}
}
runMain()
在终端运行这条命令npx hardhat run scripts/deploy.js --network ropsten
,如果你的终端输出以下结果,那么你的部署已经成功了,并且在smart_contract根目录下应该会生成一个新的文件config.js,里面保存了这个合约地址
目前为止,合约已经准备就绪,接下来我们开始搭建前端界面
用户界面
cd client
npm create vite@latest
这里需要选择react项目,本项目用的是js版本,你也可以使用ts版本- 安装项目依赖
npm i axios ethers ipfs-http-client react react-dom react-router-dom
,npm i -D tailwindcss postcss autoprefixer
- 启动项目
npm run dev
接下来配置tailwindcss
npx tailwindcss init
- 打开
tailwind.config.cjs
文件,粘贴下面的代码
module.exports = {
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
"./components/*.jsx"
],
theme: {
extend: {},
},
plugins: [],
}
- 打开index.css,删除原有代码,粘贴以下代码
@tailwind base;
@tailwind components;
@tailwind utilities;
接下来可以按官网示例编写代码,测试是否配置成功。
现在在client根目录下新建utils/contants.js
,以及utils/NFTMarketplace.json
,将smart_contract/artifacts/contracts/NFTMarketplace.sol
目录下的ABI文件NFTMarketplace.json
中的内容复制到utils/NFTMarketplace.json
里面,接下来,将以下代码粘贴到你的utils/contants.js
文件中
import NFTMarketplaceAbi from './NFTMarketplace.json'
//this is the address of your contract in ropsten test network,please replace it
export const NFTMarketplaceAddress = '0x7356659757D92E06d50ee502Eb1e5f463E581b0d'
export const NFTMarketplaceABI = NFTMarketplaceAbi.abi
好了,一切就绪,可以编写前端代码了。前端总共有四个页面,market
,create
,mine
,mylisted
,分别用来展示上架的nft,创造你自己的nft,展示你已经拥有的nft,展示你上架的nft。我们先来看看效果。
首先我们连接上metamask钱包,可以看到,目前没有任何nft在售,接下来到create页面按要求创建你的nft,并等待区块确认,页面会自动跳转到market页
可以看到我们与以太坊的交互成功了,我成功创建了我的第一个nft,并上架销售。
现在我们回到代码,首先新建一个context/NFTMarketplaceContext.jsx
文件,我们要用context传值,这样就不用一级一级传递props了,这个需要一定的react基础,在这个文件中粘贴这个代码,在这段代码中,我们首先检测是否安装了metamask客户端,接下来定义了一个connectwallet
方法,用于连接当前用户,当然最重要的是实例化了合约。接下来我们就可以在各个页面获取这个合约实例了。现在我们可以输出一下这个合约实例。
可以看到里面有我们在NFTMarketplace.sol中定义的各个方法,还有从ERC720等合约中继承过来的方法。
接下来我们只需要在需要的地方使用这些方法即可,比如在market
页面中,我们使用了fetchMarketItems
这个方法,用法如下
const data = await NFTMarketplaceContract.fetchMarketItems()
console.log(data)
输出如下
可以看到,这就是我们之前上架的那个nft的信息。
到这一步,剩下的就是将取出的信息进行页面展示了,这里就不做介绍了,大家可以自己编写,或者直接复制我的代码,本文的目的是教大家搭建nftmarketplace,理论的东西大家可以后面慢慢摸索。
合约管理系统
作为合约的所有者,你可能想看到每一笔交易的详细信息,这个时候,我们就需要搭建一个合约管理系统,本文的合约管理系统只适用于当前合约,大家可以自己拓展。
合约管理系统的项目初始化和用户交互界面类似,这里就不多做介绍了。
首先让我们看看实现的效果
可以看到,我们已经可以拿到合约的信息了,比如第一页
properties
中显示ETH Balance为0.025,也就是合约的余额是0.025,因为用户上架nft需要向合约交一笔‘手续费’(listingprice),而这笔手续费就是0.025,并且你也可以看到目前我上架的nftfetchItemsListed
,在售的nftfetchMarketItems
,在call方法中,我们需要输入参数进行查询,比如tokenURI,我之前上架的tokenid是1,查询这个nft的uri,可以看到返回。
这里最重要的是,我们如何区分这四种方法,还记得我们之前输出的NFTMarketplaceContract实例吗,这些方法就是这个实例里面的。
const NFTMarketplaceContractInterface = new ethers.utils.Interface(NFTMarketplaceABI)
//get all the functions
const { functions } = NFTMarketplaceContractInterface
//get all the events
const { events } = NFTMarketplaceContractInterface
通过这段代码,我们可以区分functions
和events
,但是properties
,sendmethod
,callmethod
如何区分呢?
//properties
functions[item].constant === true && functions[item].inputs.length === 0
//call
functions[item].constant === true && functions[item].inputs.length > 0
//send
functions[item].constant === false
as you can see,we can distinguish them like that.好了,区分了这四种方法之后,接下来又是编写前端页面和合约进行交互了,这部分就不介绍了。
整体效果
上面我们以及上架了一个nft了,现在我们用另一个账号登录页面,可以看到,mylisted
页面什么都没有
到market页面点击buy,签名,等待合约执行完成,可以看到一下变化,
mine
页面展示了刚刚购买的nft
回到之前那个账号,可以看到,
mylisted
已经什么都没有了
再来到我们的合约管理系统
我们可以查到详细信息。
让我们再试一下合约管理系统的send方法,比如我作为合约的拥有者想更改交易的手续费,我可以调用updateListingPrice
,如下
我们再查看
listingprice
,可以看到,已经变成1ETH
了
现在我们可以通过交易哈希去ropsten test network Etherscan
查看每一笔交易更加详细的的信息
这个是我点击购买的那个事件,可以看到里面详细记录了该nft从一个账户转到了另一个账户,交易了
0.1Ether
,这正好就是售卖者出售的价格,下面还有这笔交易所需的gas费
总结
以上就是本文的全部内容了,后面我会逐步完善github代码,以及添加注释。都看完了,给我的github一个小星星呀!