前言继上一篇《ReactNativeDApp开发全栈实战·从0到1系列(NFT交易所-合约部分)》,本文进入“前端交互”环节:用ethers.js一行行打通铸造→授权→挂单→购买的完整闭环。示例拆成三步:预览:balanceOf拉取用户NFT,token
继上一篇《React Native DApp 开发全栈实战·从 0 到 1 系列(NFT交易所-合约部分)》,本文进入“前端交互”环节:用 ethers.js 一行行打通 铸造 → 授权 → 挂单 → 购买 的完整闭环。示例拆成三步:
- 预览:
balanceOf
拉取用户 NFT,tokenURI
解析 IPFS 元数据,直接渲染。- 上架:
create
铸造新 NFT;approve
一键授权给市场合约;list
挂单待售。- 购买:买家调用
buy
,付款即成交,链上所有权瞬间转移。前置准备
代码说明:
前端展示
import { abi } from "@/abi/SimpleClosedNFT.json";
import * as ethers from "ethers";
const [NFTList,setNFTList]=useState([])
const nftListFn= async ()=>{
const provider = new ethers.providers.Web3Provider(window.ethereum);
await provider.send('eth_requestAccounts', []); // 唤起钱包
const signer = await provider.getSigner();
const nft = new ethers.Contract("0xa82fF9aFd8f496c3d6ac40E2a0F282E47488CFc9",abi, signer);//合约地址、abi、
const userAddress=await signer.getAddress()
console.log("当前用户",userAddress)
const nftLength= await nft.balanceOf(userAddress)
const nftList=[]
for(let i=0;i<nftLength.toString();i++){
nftList.push(await nft.tokenURI(i+1))
}
let nftArr=[]
nftList.map((item,index)=>{
fetch(item).then(r => r.json()).then(data=>{
nftArr.push({...data,id:index+1})
setNFTList([...nftArr])
})//解析ipfs上传的json文件
})
}
useEffect(()=>{
nftListFn()
},[])
代码说明:
3.把授权的nft挂单
import { abi as MarketplaceABI } from "@/abi/OZMarketplace.json";
import { abi as NFTABI } from "@/abi/SimpleClosedNFT.json";
import * as ethers from 'ethers';
const mintNFT=async ()=>{
const provider = new ethers.providers.Web3Provider(window.ethereum);
console.log("创建NFT")
await provider.send('eth_requestAccounts', []); // 唤起钱包
const signer = await provider.getSigner();
const nftAddress="0xa82fF9aFd8f496c3d6ac40E2a0F282E47488CFc9"
const nft = new ethers.Contract("0xa82fF9aFd8f496c3d6ac40E2a0F282E47488CFc9",NFTABI, signer);//nft合约
const sellContract = new ethers.Contract("0x1613beB3B2C4f22Ee086B2b38C1476A3cE7f78E8", MarketplaceABI, signer);//nft交易合约
const uri = "https://zygomorphic-magenta-bobolink.myfilebase.com/ipfs/QmQT8VpmWQVhUhoDCEK1mdHXaFaJ3KawkRxHm96GUhrXLB"//ipfs文件
const price = ethers.utils.parseEther(priceV.toString());//数字价格
console.log(price)
try{
const tx= await nft.create(uri,price,royalty,{value:price})
const receipt = await tx.wait()
const tokenId = receipt.logs.map(l => nft.interface.parseLog(l)).find(e => e.name === "Transfer")?.args[2];//铸造成功后获取nft的id
console.log("tokenId =", tokenId.toString());
console.log(await nft.tokenURI(tokenId.toString()))//查看ipfs相关信息
console.log("nft的所有者",await nft.ownerOf(tokenId.toString()))
const mintPrice=await nft.mintPrice(tokenId.toString())
console.log("nft的价格",ethers.utils.formatEther(mintPrice))
//授权
const txApprove=await nft.approve("0x1613beB3B2C4f22Ee086B2b38C1476A3cE7f78E8", tokenId.toString());//把铸造的nft授权交易合约
const receiptApprove = await txApprove.wait();
console.log("授权成功","gas消耗:",receiptApprove.gasUsed , "gas价格:",receiptApprove.gasPrice)
console.log("查看授权地址",await nft.getApproved(tokenId.toString()))
//挂单
const listTx=await sellContract.list(tokenId.toString(),ethers.utils.parseEther("1"))
console.log("挂单成功",await listTx.wait())
router.push({
pathname:"/home/nftDetails",
params:{
tokenId:tokenId.toString(),
}
})
}catch(err){
console.log(err)
}
}
代码说明:
import { abi as MarketplaceABI } from "@/abi/OZMarketplace.json";
import { abi as NFTABI } from "@/abi/SimpleClosedNFT.json";
import { useLocalSearchParams, useRouter } from 'expo-router';
import * as ethers from 'ethers';
const { tokenId } = useLocalSearchParams();//获取交易的tokenid
const sellFn= async ()=>{
const provider = new ethers.providers.Web3Provider(window.ethereum);
await provider.send('eth_requestAccounts', []); // 唤起钱包
const signer = await provider.getSigner();
const user = await signer.getAddress();
console.log("当前账户", user);
const nftContract = new ethers.Contract("0xa82fF9aFd8f496c3d6ac40E2a0F282E47488CFc9", NFTABI, signer);//nft合约 const sellContract = new ethers.Contract("0x1613beB3B2C4f22Ee086B2b38C1476A3cE7f78E8", MarketplaceABI, signer);//nft交易合约 const owner = await nftContract.ownerOf(1); console.log("所有者",owner) const price = ethers.utils.parseEther("1"); //售卖 const selltx=await sellContract.buy(tokenId,{value:price}) const selltxsuccess = await selltx.wait(); console.log("购买成功","gas消耗:",selltxsuccess.gasUsed , "gas价格:",selltxsuccess.gasPrice) console.log("nft的所有者",await nftContract.ownerOf(tokenId)) }
# 效果图
<div style="display:flex; gap:8px;flex-wrap:wrap;">
<img src="https://p0-xtjj-private.juejin.cn/tos-cn-i-73owjymdk6/328270590c6548068d04109b28e83c02~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5pyo6KW_:q75.awebp?policy=eyJ2bSI6MywidWlkIjoiMjQzNjE3MzQ5Njg0NTU0OSJ9&rk3s=f64ab15b&x-orig-authkey=f32326d3454f2ac7e96d3d06cdbb035152127018&x-orig-expires=1757323252&x-orig-sign=gaaaLkrz0amC9aPcaBHgaIkulKw%3D" alt="图1转存失败,建议直接上传图片文件" width="200">
<img src="https://p0-xtjj-private.juejin.cn/tos-cn-i-73owjymdk6/feaee4270ead4015ab8f25caa644ba6a~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5pyo6KW_:q75.awebp?policy=eyJ2bSI6MywidWlkIjoiMjQzNjE3MzQ5Njg0NTU0OSJ9&rk3s=f64ab15b&x-orig-authkey=f32326d3454f2ac7e96d3d06cdbb035152127018&x-orig-expires=1757323252&x-orig-sign=fFzSG5L7Y6PCGjH2OFUzHItmQ6w%3D" alt="图2转存失败,建议直接上传图片文件" width="200">
<img src="https://p0-xtjj-private.juejin.cn/tos-cn-i-73owjymdk6/d9dc976eb1234fb396e08ab428027528~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5pyo6KW_:q75.awebp?policy=eyJ2bSI6MywidWlkIjoiMjQzNjE3MzQ5Njg0NTU0OSJ9&rk3s=f64ab15b&x-orig-authkey=f32326d3454f2ac7e96d3d06cdbb035152127018&x-orig-expires=1757323252&x-orig-sign=NsE4VOoK0qZhYo99I0rENn%2Fdv1s%3D" alt="图3转存失败,建议直接上传图片文件" width="200">
<img src="https://p0-xtjj-private.juejin.cn/tos-cn-i-73owjymdk6/b49cd86c573946968c72195349d2a787~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5pyo6KW_:q75.awebp?policy=eyJ2bSI6MywidWlkIjoiMjQzNjE3MzQ5Njg0NTU0OSJ9&rk3s=f64ab15b&x-orig-authkey=f32326d3454f2ac7e96d3d06cdbb035152127018&x-orig-expires=1757323252&x-orig-sign=8aBjOv4HRsboK3wr8jfGd3ptM%2BA%3D" alt="图3转存失败,建议直接上传图片文件" width="200">
</div>
# 总结
#### 快速预览表
| 步骤 | 核心操作 | 关键 API |
| ----- | ----------- | ------------------------------------------ |
| 1. **预览** | **查询用户 NFT 列表** | `balanceOf` + `tokenURI` |
| 2. **铸造** | **创建 NFT** | `nft.create(uri, price, royalty, {value})` |
| 3. **授权** | **授权给市场合约** | `nft.approve(marketplace, tokenId)` |
| 4. **挂单** | **把 NFT 挂到市场** | `marketplace.list(tokenId, price)` |
| 5. **购买** | **买家支付购买** | `marketplace.buy(tokenId, {value})` |
以上就是前端与合约交互的全部过程,整个流程遵循 **“铸造 → 授权 → 挂单 → 购买”** 的标准 NFT 交易闭环,代码可直接运行于 Hardhat 本地节点,便于本地调试与二次开发。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!