React Native DApp 开发全栈实战·从 0 到 1 系列(NFT交易所-前端部分)

  • 木西
  • 发布于 15小时前
  • 阅读 71

前言继上一篇《ReactNativeDApp开发全栈实战·从0到1系列(NFT交易所-合约部分)》,本文进入“前端交互”环节:用ethers.js一行行打通铸造→授权→挂单→购买的完整闭环。示例拆成三步:预览:balanceOf拉取用户NFT,token

前言

继上一篇《React Native DApp 开发全栈实战·从 0 到 1 系列(NFT交易所-合约部分)》,本文进入“前端交互”环节:用 ethers.js 一行行打通 铸造 → 授权 → 挂单 → 购买 的完整闭环。示例拆成三步:

  1. 预览balanceOf 拉取用户 NFT,tokenURI 解析 IPFS 元数据,直接渲染。
  2. 上架
    • create 铸造新 NFT;
    • approve 一键授权给市场合约;
    • list 挂单待售。
  3. 购买:买家调用 buy,付款即成交,链上所有权瞬间转移。

    前置准备

  • hardhat启动网络节点npx hardhat node
  • 合约编译npx hardhat compile 生成对应的xxx.json用获取abi等相关信息
  • 合约部署npx hardhat deploy --tags MyNFT,OZMarketplace 获取合约地址(NFT和交易NFT的合约地址)
  • 节点的私钥导入钱包用来与合约交互时支付对应的gas费

    核心代码

    代码说明

  • 拿到当前账号的balanceOf
  • 循环tokenid数组拿到ipfs文件
  • 解析ipfs文件
  • 前端展示

    1.预览铸造的NFT

    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()
    },[])

    2.NFT铸造、授权、挂单

    代码说明

  • 1.先铸造一个nft
  • 2.在把铸造nft授权给nft交易合约
  • 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)
        }
    
    }

    3.购买NFT

    代码说明

  • 1.获取交易tokenid
  • 2.对当前的nft进行售卖
    
    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 本地节点,便于本地调试与二次开发。
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
木西
木西
0x5D5C...2dD7
江湖只有他的大名,没有他的介绍。