mcpField
N
nft

nft

github.com/kali-decoder开发17121 次安装2 周前更新
代码仓库

关于 nft

面向 Midnight Network 开发者的 NFT 开发技能,涵盖 OpenZeppelin Compact 非同质化代币合约和原生代币操作。内容说明公开与隐私所有权模式、铸造、转移、授权及元数据处理。

主要功能

  1. 01提供 ERC721 类 Compact 合约
  2. 02涵盖铸造、转移与授权
  3. 03支持公开及隐私所有权模式
  4. 04处理代币 URI 与元数据承诺
  5. 05说明编译、部署与测试

适用场景

  1. 01开发公开收藏品 NFT
  2. 02构建所有权保密的 NFT
  3. 03为代币配置元数据 URI
  4. 04测试 Compact NFT 合约

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/kali-decoder/midnight-skills --skill nft

安装后在 Claude Code 中输入/nft确认已加载。

详细介绍

范围 此技能涵盖 Midnight Network 上的 NFT 开发,包括 OpenZeppelin NonFungibleToken 合约(在 Compact 中实现,类似 ERC721)和 Midnight 原生代币操作(shielded/unshielded)。涵盖铸造、转账、授权、元数据 URI 以及 NFT 的隐私模式。

请严格按照以下路径放置文件:

  • contracts/NonFungibleToken.compact → OpenZeppelin 基础合约
  • contracts/MyNFT.compact → 你的自定义 NFT 合约
  • test/ → 合约单元测试

1) 安装

前置条件
  • Node.js v22.15+
  • Compact 开发工具:curl --proto '=https' --tlsv1.2 -sSf https://github.com/midnightntwrk/compact/releases/latest/download/compact-installer.sh | sh
  • 添加到 PATH:source $HOME/.local/bin/env
添加适用于 Compact 的 OpenZeppelin Contracts
mkdir my-nft-project && cd my-nft-project
git init
git submodule add https://github.com/OpenZeppelin/contracts-compact.git
cd contracts-compact && SKIP_ZK=true yarn && cd ..

2) OpenZeppelin NonFungibleToken 合约

OpenZeppelin NonFungibleToken 模块在 Compact 中提供类似 ERC721 的实现。

主要功能
功能状态说明
代币 ID 类型Uint<128>不支持 Uint256(电路限制)
转账✅可转给 ECDSA 公钥或合约地址
授权✅单个代币和操作员授权
元数据 URI✅存储单个代币的 URI
暂停✅通过 Pausable 模块实现
所有权管理✅通过 Ownable 模块实现
合约间调用❌尚不支持(请使用 _unsafeTransfer)
导入和使用
pragma language_version >= 0.21.0;

import CompactStandardLibrary;
import "./contracts-compact/node_modules/@openzeppelin-compact/contracts/src/access/Ownable"
  prefix Ownable_;
import "./contracts-compact/node_modules/@openzeppelin-compact/contracts/src/security/Pausable"
  prefix Pausable_;
import "./contracts-compact/node_modules/@openzeppelin-compact/contracts/src/token/NonFungibleToken"
  prefix NonFungibleToken_;

constructor(
  _name: Opaque<"string">,
  _symbol: Opaque<"string">,
  _initOwner: Either<ZswapCoinPublicKey, ContractAddress>,
) {
  Ownable_initialize(_initOwner);
  NonFungibleToken_initialize(_name, _symbol);
}

export circuit mint(
  to: Either<ZswapCoinPublicKey, ContractAddress>,
  tokenId: Uint<128>,
  uri: Opaque<"string">,
): [] {
  Ownable_assertOnlyOwner();
  NonFungibleToken__mint(to, tokenId);
  NonFungibleToken__setTokenURI(tokenId, uri);
}

export circuit transferFrom(
  from: Either<ZswapCoinPublicKey, ContractAddress>,
  to: Either<ZswapCoinPublicKey, ContractAddress>,
  tokenId: Uint<128>,
): [] {
  Pausable_assertNotPaused();
  NonFungibleToken_transferFrom(from, to, tokenId);
}

export circuit approve(
  to: Either<ZswapCoinPublicKey, ContractAddress>,
  tokenId: Uint<128>,
): [] {
  NonFungibleToken_approve(to, tokenId);
}

export circuit setApprovalForAll(
  operator: Either<ZswapCoinPublicKey, ContractAddress>,
  approved: Boolean,
): [] {
  Ownable_assertOnlyOwner();
  NonFungibleToken_setApprovalForAll(operator, approved);
}

export circuit pause(): [] {
  Ownable_assertOnlyOwner();
  Pausable__pause();
}

export circuit unpause(): [] {
  Ownable_assertOnlyOwner();
  Pausable__unpause();
}

3) Midnight 原生 NFT 模式

Midnight 标准库提供用于 shielded/unshielded 操作的原生代币函数。

Unshielded NFT(公开)
import CompactStandardLibrary;

export ledger nextTokenId: Uint<128>;
export ledger owners: Map<Uint<128>, Either<ZswapCoinPublicKey, ContractAddress>>;
export ledger tokenURIs: Map<Uint<128>, Opaque<"string">>;

export circuit mint(
  to: Either<ZswapCoinPublicKey, ContractAddress>,
  uri: Opaque<"string">,
): Uint<128> {
  const tokenId = nextTokenId;
  nextTokenId += 1;
  owners.insert(disclose(tokenId), disclose(to));
  tokenURIs.insert(disclose(tokenId), disclose(uri));
  return tokenId;
}

export circuit transfer(
  from: Either<ZswapCoinPublicKey, ContractAddress>,
  to: Either<ZswapCoinPublicKey, ContractAddress>,
  tokenId: Uint<128>,
): [] {
  const owner = owners.lookup(disclose(tokenId));
  assert(owner == disclose(from), "Not owner");
  owners.insert(disclose(tokenId), disclose(to));
}

export circuit tokenURI(tokenId: Uint<128>): Opaque<"string"> {
  return tokenURIs.lookup(disclose(tokenId));
}
Shielded NFT(私有所有权)

使用 Compact 标准库的 shielded 代币函数:

import CompactStandardLibrary;

// Mint a shielded NFT
export circuit mintShielded(
  to: ZswapCoinPublicKey,
  metadataHash: Field,
): Uint<128> {
  const tokenId = nextTokenId;
  nextTokenId += 1;

  // Store commitment to ownership + metadata
  const commitment = persistentCommit<Uint<128>>(tokenId, freshNonce());
  shieldedCommitments.insert(disclose(to), commitment);

  return tokenId;
}

// Transfer shielded NFT (prove ownership without revealing tokenId publicly)
export circuit transferShielded(
  tokenId: Uint<128>,
  newOwner: ZswapCoinPublicKey,
): [] {
  // Use ZK proof to demonstrate ownership
  // Owner's shielded wallet signs the transfer
  const oldCommitment = shieldedCommitments.lookup(disclose(callerPublicKey()));
  assert(oldCommitment != default<Field>(), "Not owner");

  shieldedCommitments.remove(disclose(callerPublicKey()));
  shieldedCommitments.insert(disclose(newOwner), oldCommitment);
}

4) 编译合约

cd contracts
compact compile MyNFT.compact artifacts/MyNFT

预期输出:

Compiling 5 circuits:
  circuit "mint" (k=11, rows=1180)
  circuit "transferFrom" (k=11, rows=1966)
  circuit "approve" (k=10, rows=966)
  circuit "pause" (k=10, rows=125)
  circuit "unpause" (k=10, rows=121)
Overall progress [====================] 5/5

5) 部署和交互(TypeScript)

import { CompiledContract } from '@midnight-ntwrk/compact-js';
import { deployContract } from '@midnight-ntwrk/midnight-js-contracts';
import { Contract } from './contracts/managed/MyNFT';

const compiledContract = CompiledContract.make('MyNFT', Contract).pipe(
  CompiledContract.withVacantWitnesses,
  CompiledContract.withCompiledFileAssets('./contracts/managed/MyNFT'),
);

const providers = await buildProviders(/* see midnight-js skill */);
const deployed = await deployContract(providers, {
  compiledContract,
  privateStateId: 'nftPrivateState',
  initialPrivateState: {},
});

console.log('NFT Contract:', deployed.deployTxData.public.contractAddress);

// Mint an NFT
const mintResult = await deployed.callTx.mint(
  { type: 'left', value: callerPublicKey() },
  1n, // tokenId
  'https://my-nft.com/metadata/1.json',
);

6) Shielded 与 Unshielded NFT

方面Unshielded(公开)Shielded(私有)
所有权在链上公开私有,仅所有者知晓
转账所有人可见隐藏,由 ZK 证明
元数据链上公开 URI可提交或哈希处理
Gas/用户成本DUST 费用DUST 费用(预览版由 1AM 赞助)
用例公共艺术品、收藏品机密资产、私有收藏品
代币 ID可见可通过承诺隐藏
何时使用 Shielded NFT
  • 所有权保密:需要对 NFT 所有者保密时
  • 私有收藏品:所有权敏感的高价值物品
  • 合规设计:仅在需要时公开所有权
何时使用 Unshielded NFT
  • 公共艺术品:所有权是其价值主张的一部分
  • 透明 DAO:作为 NFT 的治理代币
  • 标准收藏品:公开来源信息能够增加价值的场景

7) 元数据处理

公开元数据(Unshielded)
export ledger tokenURIs: Map<Uint<128>, Opaque<"string">;

export circuit setTokenURI(
  tokenId: Uint<128>,
  uri: Opaque<"string">,
): [] {
  tokenURIs.insert(disclose(tokenId), disclose(uri));
}
私有元数据(Shielded)
// Store only a commitment on-chain
export ledger metadataCommitments: Map<Uint<128>, Field>;

export circuit setPrivateMetadata(
  tokenId: Uint<128>,
  metadataHash: Field, // hash of off-chain metadata
): [] {
  const commitment = persistentCommit<Uint<128>>(tokenId, freshNonce());
  metadataCommitments.insert(disclose(tokenId), commitment);
}

8) 常见问题

问题解决方案
Uint<256> 代币 ID不支持。请使用最大为 Uint<128> 的类型(电路限制)。
合约间转账尚不支持。请谨慎使用 _unsafeTransfer。
连接基础 URI 的字符串Compact 不支持。请为每个代币存储完整 URI。
tokenId 溢出铸造前使用 assert(tokenId < MAX_ID, "ID overflow")。
URI 缺少 disclose()元数据 URI 是公开的。存储时请使用 disclose()。
Shielded 转账未提供证明所有者必须为转账签名。请使用 callerPublicKey() 验证。

9) 测试 NFT 合约

// In contracts/src/test/MyNFT.test.compact
pragma language_version >= 0.21.0;

import MyNFT;
import CompactStandardLibrary;

constructor() {
  MyNFT_initialize("MyNFT", "MNFT");
}

export circuit testMint(): [] {
  const tokenId = MyNFT_mint(disclose(callerPublicKey()), "uri1");
  assert(tokenId == 1, "First token should be 1");
}

export circuit testTransfer(): [] {
  MyNFT_transferFrom(
    callerPublicKey(),
    someOtherKey(),
    1,
  );
  const newOwner = MyNFT_ownerOf(1);
  assert(newOwner == someOtherKey(), "Transfer failed");
}

10) package.json 脚本

{
  "scripts": {
    "compact": "cd contracts && compact compile MyNFT.compact artifacts/MyNFT",
    "build": "tsc && npm run compact",
    "test": "cd contracts && compact compile MyNFT.test.compact artifacts/MyNFT.test"
  }
}

11)实用链接

评论

支持 Markdown