NFT Market Plus——使用 ERC20Permit 和 EIP-712 构建高效的 NFT 市场

  • Yiming
  • 更新于 2024-10-09 11:45
  • 阅读 535

NFT市场虽然很火,但高昂的gas费用和繁琐的操作步骤一直让用户头疼。每次交易都得授权,gas费蹭蹭往上涨,体验不太好。为了解决这些问题,我写了一个NFT市场应用:“NFTMarketPlus”,使用了ERC20Permit和EIP-712来优化gas消耗,还让操作更简单

NFT 市场虽然很火,但高昂的 gas 费用和繁琐的操作步骤一直让用户头疼。每次交易都得授权,gas 费蹭蹭往上涨,体验不太好。为了解决这些问题,我写了一个 NFT 市场应用:“NFT Market Plus”,使用了 ERC20PermitEIP-712 来优化 gas 消耗,还让操作更简单。

ERC20Permit 省去了手动授权的步骤,直接用 token 交易,省时省钱;EIP-712 通过结构化签名,让授权 NFT 转移变得更高效、更安全。我还用 Next.js 写了个前端,用户可以在浏览器上直接与智能合约交互,体验更加顺畅。

接下来我会介绍整个项目的技术细节,包括智能合约的设计、前端交互的实现、以及如何优化 gas 和提升安全性,最后给出完整的项目源码和线上预览链接。希望能给想要开发 NFT 市场的朋友们一些参考。

技术栈介绍

智能合约的开发,我选用了 foundry 作为开发框架。 ERC20 代币作为 NFT 市场的基础支付货币,交易对象为 ERC721 标准的 NFT。相关功能依赖了 openzeppelin 库,如 ERC20PermitEIP712ECDSA 等。智能合约部署在 Sepolia 测试网。

前端开发使用了基于 React 框架的 Next.js 。使用了 RainbowKitviemwagmi 来实现 Web3 相关的交互。TailwindCSSNextUI 负责样式和 UI 组件。。同时,项目还引入了 TypeScript 进行类型检查,并使用了 Lucide 图标库和 React Hot Toast 来显示通知。前端页面托管和部署在 Vercel

智能合约设计与实现

智能合约包括三个文件:

  • YMToken.solERC20 代币作为 NFT Market Plus 的基础支付货币
  • YMNFT.sol:示例 NFT,作为 NFT Market Plus 的交易对象
  • NFTMarketPlus.sol:NFT Market Plus 主体

YMToken

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";

contract YMToken is ERC20, ERC20Burnable, Ownable, ERC20Permit {
    constructor(address initialOwner)
        ERC20("YMToken", "YMT")
        Ownable(initialOwner)
        ERC20Permit("YMToken")
    {}

    function mint(address to, uint256 amount) public {
        _mint(to, amount);
    }
}

使用 openzeppelin 的模板生成器生成,相较普通的 ERC20 代币,多了线下 Permit 授权的功能。

YMNFT

// SPDX-License-Identifier: MIT
// Compatible with OpenZeppelin Contracts ^5.0.0
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

同样使用 openzeppelin 的模板生成器生成,相较普通的 ERC721,额外支持了 EIP712

    function permit(
        address _to,
        uint256 _tokenId,
        uint256 _deadline, 
        bytes calldata _signature
    ) public returns (address) {
        // verify the signature
        bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
            LIST_TYPEHASH,
            _to,
            _tokenId,
            _deadline
        )));
        address signer = ECDSA.recover(digest, _signature);
        require(ownerOf(_tokenId) == signer, "Signer is not the owner of the NFT");
        _approve(_to, _tokenId, address(0));
        return signer;
    }

添加了 permit 方法来支持链上验证签名。

NFTMarketPlus

    function permitBuy(address _nftAddr, uint256 _tokenId, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
        require(block.timestamp <= deadline, "Signature expired");
        token.permit(
          msg.sender, 
          address(this), 
          nftList[_nftAddr][_tokenId].price,
          deadline,
          v, r, s
        );
        buy(_nftAddr, _tokenId);
    }

permitBuy 方法在进行 buy 操作之前,先使用 ERC20Permit 里的 permit 方法验证传进来的签名数据是否正确。正确则直接进行 buy 操作;否则交易回退。相比于传统交易方式,少了手动 approve 的操作,既方便了交互,又节约了 gas。

    function listWithSignature(
        address _nftAddr,
        uint256 _tokenId,
        uint256 _price,
        uint256 _deadline, 
        bytes calldata _signature
    ) public {
        require(block.timestamp <= _deadline, "Signature expired");
        // approve
        address signer = INFT(_nftAddr).permit(address(this), _tokenId, _deadline, _signature);
        // list the NFT
        _list(signer, _nftAddr, _tokenId, _price);
    }

listWithSignature 在 NFT 挂单前,调用了 YMNFTpermit 方法,使用 EIP712 结构化签名来验证交易信息。只有当签名者是 NFT 的所有者,挂单才能成功,否则将失败回退。

前端集成和用户交互

前端整体分为三个页面:

Market 页面用于展示已上架的 NFT。用户可以在该页面购买 NFT,NFT 所有者也可以在这里取消已上架的 NFT。

image.png

Profile 页面用于管理用户资产。用户可以在此查询 YMToken 代币数量、通过水龙头免费领取 1000 代币,以及查看自己拥有的 NFT 并进行挂单上架操作。

image.png

Admin 页面只对管理员开发。管理员可以在此向任意地址铸造任意代币和 NFT。

image.png

当我们需要将 NFT 上架时,在 Profile 页面点击 List 按钮,输入想要挂单的价格,点击 Submit,会在钱包插件中弹出签名确认信息,确认后生成签名,然后进入到上架操作,在钱包中点击确认,验证签名正确,就能上架成功,一会儿后即可在Market 页面看到刚刚上架的 NFT。

image.png

当我们需要购买 NFT 时,在 Market 页面点击 Buy 按钮,也会在钱包插件中弹出签名确认信息,生成签名,然后在购买操作中验证签名。如果签名正确且你有足够的 token,就能购买成功,该 NFT 会转移到你的地址,你可在 Profile 中查看确认。

image.png

我们可以注意到,使用 EIP-712ERC20Permit 进行上架和购买操作,全程无需额外的 approve,授权、验证、买/卖只需要一步搞定,非常顺滑流畅,极大提高了用户的操作体验,也节约了宝贵的 gas 手续费。

代码展示和项目预览

点赞 3
收藏 2
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Yiming
Yiming
0xCE73...f73E
江湖只有他的大名,没有他的介绍。