11.NFT Market后端开发
思路¶
1.获取title和description和图片
2.上传图片
3.生成metadata并上传ipfs
4.mint NFT给用户
5.返回metadata给用户
初始化后端¶
初始化项目
npm init -y
安装expressjs和模板引擎ejs
npm install express
npm install ejs
npm install --save-dev nodemon
{
"main": "app.js",
"scripts": {
"start":"nodemon app.js"
},
启动
npm run start
前端准备¶
先准备简单的前端用于交互
// views/home.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NFT Market</title>
</head>
<body>
<h1>Upload file to IPFS</h1>
<form action="/upload" method="POST" enctype="multipart/form-data">
<label>Title</label>
<input type="text" name="title">
<br/>
<label>Description</label>
<input type="text" name="description">
<br/>
<input type="file" name="file">
<br/>
<input type="submit" name="Submit">
</form>
</body>
</html>
渲染到页面
// const express=require('express')
import express from 'express';//需要在package.json增加type:module
const app=express();
app.set('view engine','ejs')
app.get('/',function (req,res){
res.render("home")
})
const port=3002;
app.listen(port,()=>{
console.log(`Server is running on ${port}..`)
})
upload接口¶
body-parser: 解析body的中间件
express-fileupload:处理上传的文件
npm install body-parser
npm i express-fileupload
配置
import bodyParser from 'body-parser';
import fileUpload from 'express-fileupload'
...
// body处理配置
app.use(bodyParser.urlencoded({extended:true}))
app.use(fileUpload())
接口
app.post('/upload',function (req,res){
console.log(req.body)
})
测试
细化接口
IPFS处理图片
用户上传图片,我需要将图片先保存到本地,再上传IPFS
图片保存本地¶
app.post('/upload',function (req,res){
const title=req.body.title;
const description=req.body.description;
const file=req.files.file;
const filename=file.name;
const filePath="files/"+filename;
//用户图片转移到本地
file.mv(filePath,(err)=>{
if(err){
console.log(err);
res.status(500).send("error occured")
}
})
res.json({
message:"file upload successfully"
})
})
IPFS引入¶
library: https://github.com/ipfs/js-kubo-rpc-client
安装
npm i kubo-rpc-client
启动本地ipfs节点
代码
// ipfs-uploader.js
import { create } from 'kubo-rpc-client'
import fs from 'fs'
// connect using a URL
const ipfs = create(new URL('http://127.0.0.1:5001'))
async function uploadFileToIPFS(filePath){
const file=fs.readFileSync(filePath);
const result=await ipfs.add({
path:filePath,
content: file
})
console.log(result);
return result;
}
uploadFileToIPFS("files/MetaMask.png");
测试
node ipfs-uploader.js
{
path: 'files',
cid: CID(QmRitRTAXnADAsKdeJXn3cFeY4ZRW2s3Td8rm4d9HZfhPm),
size: 35499
}
访问localhost:8080/ipfs/QmRitRTAXnADAsKdeJXn3cFeY4ZRW2s3Td8rm4d9HZfhPm
这个case中,我们要封装metadata给用户
把这两个函数export出去,到app.js调用
import { create } from 'kubo-rpc-client'
import fs from 'fs'
// connect using a URL
const ipfs = create(new URL('http://127.0.0.1:5001'))
export async function uploadFileToIPFS(filePath){
const file=fs.readFileSync(filePath);
const result=await ipfs.add({
path:filePath,
content: file
})
return result;
}
export async function uploadJSONToIPFS(json){
const result =await ipfs.add(JSON.stringify(json));
return result;
}
app.js引入
import { uploadFileToIPFS,uploadFileToIPFS } from './ipfs-uploader.js';
app.post('/upload',function (req,res){
const title=req.body.title;
const description=req.body.description;
const file=req.files.file;
const filename=file.name;
const filePath="files/"+filename;
//用户图片转移到本地
file.mv(filePath,async(err)=>{
if(err){
console.log(err);
res.status(500).send("error occured")
}
const fileResult=await uploadFileToIPFS(filePath);
const fileCid=fileResult.cid.toString();
const metadata={
title:title,
description:description,
image:'http://127.0.0.1:8080/ipfs/'+fileCid
}
const metadataResult=await uploadJSONToIPFS(metadata);
const metadataCid=metadataResult.cid.toString();
console.log(metadataCid);
res.json({
message:"file upload successfully",
data:metadata
})
})
})
转发NFT¶
用ethers.js和智能合约交互
npm install ethers
部署合约,copy abi放在abis/MyNFT.json
创建nft-minter.js
import {ethers,JsonRpcProvider} from "ethers"
import fs from 'fs'
async function mint(to,uri){
const provider=new JsonRpcProvider("http://127.0.0.1:8545");
const signer=await provider.getSigner();
const contractAddress="0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266";
const abi=JSON.parse(fs.readFileSync("./abis/MyNFT.json"));
const contract=new ethers.Contract(contractAddress,abi,signer);
const result=await contract.safeMint(to,uri);
console.log(result);
}
mint("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266","http://github,com")
启动本地节点测试
npx hardhat node
node nft-minter.js
ContractTransactionResponse {
provider: JsonRpcProvider {},
blockNumber: 6,
blockHash: '0x5d980b2c1d0601508e350f1f6597182fc07f0a30c7cbe76eb664629ae196ae4e',
index: undefined,
hash: '0xde57168afbf8fcd89955e4b36ec5825c5653156ded37a40c275f1dff19c3fe31',
type: 2,
to: '0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9',
from: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
nonce: 4,
gasLimit: 151230n,
gasPrice: 1482295161n,
maxPriorityFeePerGas: 1000000000n,
maxFeePerGas: 1964590322n,
data: '0xd204c45e000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000011687474703a2f2f6769746875622c636f6d000000000000000000000000000000',
value: 0n,
chainId: 31337n,
signature: Signature { r: "0xc8d9f6ff56eac75ac971929173b5263b58185668ee95b2813cace45de96ab6d4", s: "0x3f816ad824ca989c2d0d58d3148c198a7de4bc203a8ad1b9ad3e62fcbfea4769", yParity: 1, networkV: null },
accessList: []
}
整理代码,export出去
import {ethers,JsonRpcProvider} from "ethers"
import fs from 'fs'
export async function mint(to,uri){
const provider=new JsonRpcProvider("http://127.0.0.1:8545");
const signer=await provider.getSigner();
const contractAddress="0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9";
const abi=JSON.parse(fs.readFileSync("./abis/MyNFT.json"));
const contract=new ethers.Contract(contractAddress,abi,signer);
const result=await contract.safeMint(to,uri);
console.log(result);
}
// app.js
import { mint } from './nft-minter.js';
...
await mint("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266","http://127.0.0.1:8080/ipfs/"+metadataCid)
优化¶
把环境变量提出出来,易于后续修改
管理环境变量的库:dotenv
npm install dotenv --save
跨域¶
npm install cors
import cors from 'cors';
app.use(cors())