锁仓与碎片的艺术博弈:Unipeg 推出 microPEG 核心攻克了什么痛点?

7 阅读11分钟

前言

在 Web3 的图币混合(Hybrid Assets)与全链上生成艺术(Generative Art)赛道中,Unipeg 凭借其“买卖代币即创造艺术、持仓数字即艺术品本身”的独特模式,成为了近期加密市场瞩目的焦点。

为了进一步打破 ERC-20 与 NFT 的界限,Unipeg 官方于近期重磅推出了 microPEG(微型 NFT 铸造与通缩)功能 [85615]。用户只需封装(Wrap)0.1 枚 $uPEG 代币,即可在链上实时渲染并铸造一枚 12×12 像素的微型 NFT(mPEG)。

这一新功能的推出不仅是一次技术升级,更是项目方在代币经济学(Tokenomics)和全链上博弈上的一次精准出击。那么,在拆解 microPEG 的降维打击之前,我们有必要先复盘一下 uPEG 协议本身的底层逻辑。


📌 前情回顾:往期 uPEG 原生协议底层核心拆解

根据我们此前对 Unipeg 的深度评测,该协议最硬核的突破在于彻底抛弃了传统 NFT 对中心化服务器(如 IPFS 或 AWS)的依赖,并打破了传统 ERC-404 的死板映射。它的底层技术是由以下三大支柱拧合而成的:

  1. Uniswap v4 Hook(钩子)作为“实时美术渲染器”
    传统的去中心化交易所只能做冰冷的资产数字加减。而 uPEG 巧妙利用了 Uniswap v4 的生命周期 Hook 机制。当用户在流动性池中执行 Swap(买入或卖出) $uPEG 的那一刻,Hook 会立刻捕获当前的交易哈希(Tx Hash)作为随机数种子。合约直接在以太坊虚拟机(EVM)内用代码实时计算,渲染出一张 24×24 像素的 SVG 独角兽图像

  2. “数字余额”与“图像刻度”的整数拧合
    uPEG 没有任何传统的 Token ID 编号概念。它将资产余额变成了图像的“开关”:

    • 整数长出形体:如果你的钱包里持有 2.5 枚 $uPEG,你的账户只会激活并显示对应的 2 只“完整独角兽”。
    • 小数退化为碎片(Dust) :多出来的 0.5 枚代币仅仅是普通的数字资产,没有任何艺术形态。
    • 动态销毁机制:一旦你把代币卖回池子导致余额降到 1.9 枚,第 2 只独角兽就会在全链上被立即销毁(Burn) 。也就是说,世界上最多只能同时存在 10,000 只完整的独角兽。

正是基于这一“只有凑齐整数才能拥有艺术”的硬核设定,才衍生出了今天我们要讨论的全新升级功能——microPEG。它的诞生,正是为了精准狙击原生协议在运转过程中暴露出的行业痛点。

痛点一:高昂的“散户入场门槛”与流动性碎片化

❌ 传统痛点

在 Unipeg 的初始设定中,其原生代币 $uPEG 的最大供应量被严格限制在 10,000 枚。用户必须持有 1 枚完整的整币,钱包里才能动态“长出”一只全链上独角兽 NFT。
随着代币价格在市场博弈中水涨船高,1 枚整币的价格对于普通散户或轻度参与者而言变得过于高昂。那些持有 0.2 或 0.8 枚代币的散户,在前端无法激活任何图形展示,代币退化成了纯粹的数字投机,失去了全链上艺术的核心趣味,导致用户生态阶层严重割裂。

microPEG 的解法

microPEG 将门槛直接降低了 10 倍 。现在,散户哪怕只持有碎片化的代币,也可以通过封装将其转化为一枚拥有独立全链上艺术表现力的微型 NFT 。这让资金体量较小的用户也能以极低的成本深度参与到全链上艺术的收集与展示中,极大地扩大了社区的共识边界。

痛点二:“资产碎片”的经济价值处于沉睡状态

❌ 传统痛点

在过往的图币混合模型中,用户在买卖或交互过程中不可避免地会产生各种“非整数碎片”(例如持仓 2.45 枚代币,其中 0.45 枚就是碎片)。
这些散落在无数钱包里的资产碎片,无法在 NFT 交易市场(如 OpenSea 或 Blur)中获得任何非同质化溢价。它们只能躺在钱包里充当普通的“数字尘埃(Dust)”,其潜在的链上图像渲染能力和艺术价值被完全浪费。

microPEG 的解法

项目方通过打造 microPEG,为这些“数字尘埃”赋予了全新的生命。它通过专属的 uPEG/mPEG 流动性池,让原本无法动弹的碎片资产变为了可公开交易、独立定价的微型 NFT。至此,碎片不再是主资产的附庸,而是拥有独立二级市场流动性的金融艺术品。

痛点三:原生代币缺乏持续的、内生的“通缩发动机”

❌ 传统痛点

对于一个总量极度稀缺(仅 1 万枚)的项目而言,如何让持币者减少抛售、主动长期锁仓,是决定项目生命周期的关键。如果缺乏一套精妙的博弈机制,资产在达到一定价格后极易因流动性枯竭或筹码松动而陷入增长瓶颈。

microPEG 的解法

这是 microPEG 最具侵略性的经济学设计——跨资产吸收与强制通缩
每当社区用户因为喜爱微型 NFT 图像、或者为了追求流动性而调用 wrap 铸造一枚 mPEG 时,那 0.1 枚 $uPEG 就会被强行锁死在封装合约中
随着市场上 microPEG 的需求量扩大,流通中的 $uPEG 现货会被源源不断地蚕食和锁仓。这导致市面上想要凑齐“1 枚整币(从而生成 24×24 大 NFT)”变得越来越困难。这种人为制造的供需杠杆,会强行拉高完整代币与整币 NFT 的稀缺度与地板价 。

痛点四:全链上艺术缺乏多维度的“社交博弈与可玩性”

❌ 传统痛点

传统的 NFT 项目,用户买入后往往只能被动“拿着(HODL)”或者“卖出”,交互维度单一。如何让社区在不依赖外部流动性挖矿的前提下,自发产生高频交易和话题度,是所有 Web3 链上实验的共同难题。

microPEG 的解法

microPEG 的出现,直接将游戏引入了“降维与升维”的博弈层面。
用户现在面临着有趣的财富与美学抉择:是保留一个完整的 24×24 大独角兽,还是选择将其拆解成 10 个 12×12 的微型独角兽去市场上售卖?由于微型 NFT 在铸造时会重新引入随机数种子渲染全新的画面,用户甚至可以通过不断的“拆分(Unwrap)- 封装(Wrap)”来反复刷新微型 NFT 的外观,从而“对赌”更稀有的图案。这种玩法极大地增加了持有者之间的换手率和社区内的自发博弈。


核心合约实现、测试、部署一站式

一、核心合约

  1. UnipegToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/Base64.sol";

contract UnipegToken is ERC20 {
    using Strings for uint256;

    uint256 public constant MAX_SUPPLY = 10000 * 10**18; // 最大1万枚

    constructor() ERC20("Unipeg", "uPEG") {
        _mint(msg.sender, MAX_SUPPLY);
    }

    // 核心逻辑:获取用户当前拥有的完整 NFT 数量(整数部分)
    function publicBalanceOfNFT(address account) public view returns (uint256) {
        return balanceOf(account) / 10**18;
    }

    // 全链上动态渲染 24x24 像素的 SVG 图像
    function renderSVG(address owner, uint256 index) public view returns (string memory) {
        // 使用持有者地址和索引生成伪随机颜色,模拟交易 Hash 改变图像
        bytes32 hash = keccak256(abi.encodePacked(owner, index));
        string memory color = string(abi.encodePacked("#", toColorHex(hash)));

        // 返回 24x24 像素的 SVG 字符串
        return string(abi.encodePacked(
            "<svg xmlns='http://w3.org' viewBox='0 0 24 24' width='100' height='100'>",
            "<rect width='24' height='24' fill='", color, "'/>",
            "<rect x='8' y='8' width='8' height='8' fill='#ffffff'/>", // 简易独角兽主体
            "<polygon points='12,2 10,8 14,8' fill='#ff00ff'/>",       // 独角兽角
            "</svg>"
        ));
    }

    // 将 SVG 封装为符合 ERC-721 标准的 Metadata JSON (Base64 编码)
    function tokenURI(address owner, uint256 index) public view returns (string memory) {
        require(index < publicBalanceOfNFT(owner), "NFT index out of bounds");
        
        string memory svg = renderSVG(owner, index);
        string memory json = Base64.encode(bytes(string(abi.encodePacked(
            '{"name": "Unipeg #', index.toString(), '", ',
            '"description": "Full Chain Generative Unipeg NFT.", ',
            '"image": "data:image/svg+xml;base64,', Base64.encode(bytes(svg)), '"}'
        ))));
        
        return string(abi.encodePacked("data:application/json;base64,", json));
    }

    // 辅助函数:将 Hash 转为十六进制颜色代码
    function toColorHex(bytes32 hash) internal pure returns (string memory) {
        bytes memory alphabet = "0123456789abcdef";
        bytes memory str = new bytes(6);
        for (uint256 i = 0; i < 3; i++) {
            str[i*2] = alphabet[uint8(hash[i] >> 4)];
            str[i*2+1] = alphabet[uint8(hash[i] & 0x0f)];
        }
        return string(str);
    }
}

  1. MicroPegWrapper.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/Base64.sol";
import "@openzeppelin/contracts/utils/Strings.sol";

contract MicroPegWrapper is ERC721 {
    using Strings for uint256;

    // 2. 【直接使用原生的 uint256 代替】
    uint256 private _currentId; 
    
    IERC20 public immutable uPEG;
    uint256 public constant WRAP_AMOUNT = 0.1 * 10**18;

    mapping(uint256 => address) public minters;

    constructor(address _uPEGAddress) ERC721("MicroPeg", "mPEG") {
        uPEG = IERC20(_uPEGAddress);
    }

    function wrap() external returns (uint256) {
        require(uPEG.transferFrom(msg.sender, address(this), WRAP_AMOUNT), "Transfer failed");

        // 3. 【修改为纯数字自增,效率更高,更安全】
        _currentId++;
        uint256 newItemId = _currentId;
        
        _safeMint(msg.sender, newItemId);
        minters[newItemId] = msg.sender;

        return newItemId;
    }

    function unwrap(uint256 tokenId) external {
        // 4. 【OpenZeppelin v5 中 _isApprovedOrOwner 已被废弃,改为检查 ownerOf】
        require(ownerOf(tokenId) == msg.sender, "Not the owner");

        _burn(tokenId);
        delete minters[tokenId];

        require(uPEG.transfer(msg.sender, WRAP_AMOUNT), "Refund failed");
    }

    function renderMicroSVG(uint256 tokenId) public view returns (string memory) {
        address minter = minters[tokenId];
        bytes32 hash = keccak256(abi.encodePacked(minter, tokenId));
        
        string memory color = string(abi.encodePacked("#", toColorHex(hash)));

        return string(abi.encodePacked(
            "<svg xmlns='http://w3.org' viewBox='0 0 12 12' width='100' height='100'>",
            "<rect width='12' height='12' fill='", color, "'/>",
            "<rect x='4' y='4' width='4' height='4' fill='#ffffff'/>",
            "</svg>"
        ));
    }

    function tokenURI(uint256 tokenId) public view override returns (string memory) {
        // 5. 【OpenZeppelin v5 中移除了 _requireMinted,直接用 ownerOf 检查即可】
        require(ownerOf(tokenId) != address(0), "ERC721: invalid token ID");
        
        string memory svg = renderMicroSVG(tokenId);
        string memory json = Base64.encode(bytes(string(abi.encodePacked(
            '{"name": "MicroPeg #', tokenId.toString(), '", ',
            '"description": "12x12 Micro NFT wrapped from 0.1 uPEG.", ',
            '"image": "data:image/svg+xml;base64,', Base64.encode(bytes(svg)), '"}'
        ))));
        
        return string(abi.encodePacked("data:application/json;base64,", json));
    }

    function toColorHex(bytes32 hash) internal pure returns (string memory) {
        bytes memory alphabet = "0123456789abcdef";
        bytes memory str = new bytes(6);
        for (uint256 i = 0; i < 3; i++) {
            str[i*2] = alphabet[uint8(hash[i] >> 4)];
            str[i*2+1] = alphabet[uint8(hash[i] & 0x0f)];
        }
        return string(str);
    }
}

二、测试脚本

  • 测试用例:Unipeg & MicroPeg Protocol Integration Test
    • 基本属性验证:uPEG 应成功初始化,且铸造最大供应量给 Owner
    • 整数 NFT 机制:持币数量跨越整数时,NFT 余额应动态正确变化
    • 封装功能(Wrap):用户应能抵押 0.1 uPEG 碎片,并成功生成 12x12 的 mPEG 微型 NFT
    • 全链上渲染验证:生成的 TokenURI 应为 Base64 编码的有效 JSON 且包含 SVG 图形
    • 解封装功能(Unwrap):销毁微型 NFT 应能原路退回 0.1 uPEG 资产
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { parseEther, getAddress } from "viem";
import { network } from "hardhat";

describe("Unipeg & MicroPeg Protocol Integration Test", function () {
  
  // 部署固件:每次测试前初始化干净的链上环境
  async function deployFixture() {
    // 1. 获取 Hardhat 提供的 Viem 客户端
    const { viem } = await network.connect();
    const [owner, otherAccount] = await viem.getWalletClients();
    const publicClient = await viem.getPublicClient();

    // 2. 部署主代币合约 uPEG
    const unipegToken = await viem.deployContract("UnipegToken");

    // 3. 部署封装合约 mPEG,并将 uPEG 的地址作为参数传入
    const microPegWrapper = await viem.deployContract("MicroPegWrapper", [unipegToken.address]);

    return {
      unipegToken,
      microPegWrapper,
      owner,
      otherAccount,
      publicClient,
    };
  }

  it("基本属性验证:uPEG 应成功初始化,且铸造最大供应量给 Owner", async function () {
    const { unipegToken, owner } = await deployFixture();
    
    const name = await unipegToken.read.name();
    const symbol = await unipegToken.read.symbol();
    const balance = await unipegToken.read.balanceOf([owner.account.address]);
    const maxSupply = await unipegToken.read.MAX_SUPPLY();

    assert.equal(name, "Unipeg");
    assert.equal(symbol, "uPEG");
    assert.equal(balance, maxSupply, "Owner 应持有全部初始代币");
  });

  it("整数 NFT 机制:持币数量跨越整数时,NFT 余额应动态正确变化", async function () {
    const { unipegToken, owner, otherAccount ,publicClient} = await deployFixture();

    // 刚开始 otherAccount 余额为 0,NFT 数量为 0
    let nftBalance = await unipegToken.read.publicBalanceOfNFT([otherAccount.account.address]);
    assert.equal(nftBalance, 0n);

    // 1. 转账 1.5 枚代币给新用户
    const hash1 = await unipegToken.write.transfer([otherAccount.account.address, parseEther("1.5")], { account: owner.account });
    await publicClient.waitForTransactionReceipt({ hash: hash1 });

    // 此时 NFT 整数部分应为 1
    nftBalance = await unipegToken.read.publicBalanceOfNFT([otherAccount.account.address]);
    assert.equal(nftBalance, 1n, "1.5 枚代币应对应 1 个 NFT");

    // 2. 再转账 0.6 枚代币(累计 2.1 枚代币)
    const hash2 = await unipegToken.write.transfer([otherAccount.account.address, parseEther("0.6")], { account: owner.account });
    await publicClient.waitForTransactionReceipt({ hash: hash2 });

    // 此时 NFT 整数部分应动态变为 2
    nftBalance = await unipegToken.read.publicBalanceOfNFT([otherAccount.account.address]);
    assert.equal(nftBalance, 2n, "2.1 枚代币应对应 2 个 NFT");
  });

  it("封装功能(Wrap):用户应能抵押 0.1 uPEG 碎片,并成功生成 12x12 的 mPEG 微型 NFT", async function () {
    const { unipegToken, microPegWrapper, owner, otherAccount ,publicClient} = await deployFixture();
    // const publicClient = await viem.getPublicClient();

    // 1. 先给 otherAccount 分配一些用于测试的碎片代币 (0.5 uPEG)
    await unipegToken.write.transfer([otherAccount.account.address, parseEther("0.5")], { account: owner.account });

    // 2. 授权(Approve):otherAccount 授权 microPegWrapper 使用其 0.1 个 uPEG
    const approveHash = await unipegToken.write.approve([microPegWrapper.address, parseEther("0.1")], { account: otherAccount.account });
    await publicClient.waitForTransactionReceipt({ hash: approveHash });

    // 3. 执行封装(Wrap):铸造微型 NFT
    const wrapHash = await microPegWrapper.write.wrap({ account: otherAccount.account });
    await publicClient.waitForTransactionReceipt({ hash: wrapHash });

    // 4. 资产检查:验证 uPEG 余额是否扣除了 0.1,且 wrapper 成功扣留了该资金
    const userLeftBalance = await unipegToken.read.balanceOf([otherAccount.account.address]);
    const wrapperLockedBalance = await unipegToken.read.balanceOf([microPegWrapper.address]);
    assert.equal(userLeftBalance, parseEther("0.4"), "用户余额应剩余 0.4");
    assert.equal(wrapperLockedBalance, parseEther("0.1"), "Wrapper 合约中应锁定 0.1(实现通缩)");

    // 5. NFT 拥有权检查:Token ID 为 1 的微型 NFT 应该属于 otherAccount
    const nftOwner = await microPegWrapper.read.ownerOf([1n]);
    assert.equal(getAddress(nftOwner), getAddress(otherAccount.account.address), "微型 NFT 的持有人不匹配");
  });

  it("全链上渲染验证:生成的 TokenURI 应为 Base64 编码的有效 JSON 且包含 SVG 图形", async function () {
    const { unipegToken, microPegWrapper, owner ,publicClient} = await deployFixture();
    // const publicClient = await viem.getPublicClient();

    // 授权并封装一个 Token
    await unipegToken.write.approve([microPegWrapper.address, parseEther("0.1")], { account: owner.account });
    await microPegWrapper.write.wrap({ account: owner.account });

    // 提取链上 Metadata
    const uri = await microPegWrapper.read.tokenURI([1n]);

    // 验证前缀是否符合标准的 Base64 Data URI 规范
    assert.ok(uri.startsWith("data:application/json;base64,"), "URI 前缀不合法");

    // 解码验证内部 JSON 内容
    const base64Content = uri.replace("data:application/json;base64,", "");
    const jsonString = Buffer.from(base64Content, "base64").toString("utf-8");
    const metadata = JSON.parse(jsonString);

    assert.equal(metadata.name, "MicroPeg #1");
    assert.ok(metadata.image.includes("data:image/svg+xml;base64,"), "JSON 内部必须包含全链上绑定的 SVG 图像");
  });

  it("解封装功能(Unwrap):销毁微型 NFT 应能原路退回 0.1 uPEG 资产", async function () {
    const { unipegToken, microPegWrapper, otherAccount, owner, publicClient } = await deployFixture();

    // 1. 初始化分配 0.1 代币并完成 wrap 质押
    await unipegToken.write.transfer([otherAccount.account.address, parseEther("0.1")], { account: owner.account });
    await unipegToken.write.approve([microPegWrapper.address, parseEther("0.1")], { account: otherAccount.account });
    await microPegWrapper.write.wrap({ account: otherAccount.account });

    // 确认此时用户余额归零
    assert.equal(await unipegToken.read.balanceOf([otherAccount.account.address]), 0n);

    // 2. 调用 unwrap 销毁 Token 1
    const unwrapHash = await microPegWrapper.write.unwrap([1n], { account: otherAccount.account });
    await publicClient.waitForTransactionReceipt({ hash: unwrapHash });

    // 3. 结果验证:NFT 被成功销毁(再次读取应该抛出错误)
    await assert.rejects(
      async () => {
        await microPegWrapper.read.ownerOf([1n]);
      },
      (err: any) => err !== undefined,
      "解封装后的 NFT 应该已被销毁"
    );

    // 4. 验证 0.1 代币原路退回
    const finalBalance = await unipegToken.read.balanceOf([otherAccount.account.address]);
    assert.equal(finalBalance, parseEther("0.1"), "解封装后代币余额未能正确返还");
  });

});

三、部署脚本

// scripts/deploy.js
import { network, artifacts } from "hardhat";
async function main() {
  // 连接网络
  const { viem } = await network.connect({ network: network.name });//指定网络进行链接
  
  // 获取客户端
  const [deployer] = await viem.getWalletClients();
  const publicClient = await viem.getPublicClient();
 
  const deployerAddress = deployer.account.address;
   console.log("部署者的地址:", deployerAddress);
  // 加载合约
  const UnipegTokenArtifact = await artifacts.readArtifact("UnipegToken");

  // 部署(构造函数参数:recipient, initialOwner)
  const UnipegTokenHash = await deployer.deployContract({
    abi: UnipegTokenArtifact.abi,//获取abi
    bytecode: UnipegTokenArtifact.bytecode,//硬编码
    args: [],//process.env.RECIPIENT, process.env.OWNER
  });

  // 等待确认并打印地址
  const UnipegTokenReceipt = await publicClient.waitForTransactionReceipt({ hash: UnipegTokenHash });
  console.log("合约地址:", UnipegTokenReceipt.contractAddress);
  const MicroPegWrapperArtifact = await artifacts.readArtifact("MicroPegWrapper");
  const MicroPegWrapperHash = await deployer.deployContract({
    abi: MicroPegWrapperArtifact.abi,//获取abi
    bytecode: MicroPegWrapperArtifact.bytecode,//硬编码
    args: [UnipegTokenReceipt.contractAddress],//tokenAddress
  });
  // 等待确认并打印地址
  const MicroPegWrapperReceipt = await publicClient.waitForTransactionReceipt({ hash: MicroPegWrapperHash });
  console.log("合约地址:", MicroPegWrapperReceipt.contractAddress);
}

main().catch(console.error);

总结

从往期拆解的 Uniswap v4 Hook 链上渲染,到如今的 microPEG 碎片艺术化,Unipeg 成功利用双向流动反馈机制,解答了图币混合赛道长期以来关于“散户门槛”与“锁仓动力”的考题。

它不仅用 0.1 枚的低门槛解决了散户无感参与的痛点,更通过精妙的封装锁仓逻辑,为原生 $uPEG 铸造了一台强大的通缩引擎。在 Web3 资产叙事日渐同质化的今天,Unipeg 的这场全链上实验无疑为混合资产的演进提供了极具参考价值的标杆范式。