Rust区块链NFT开发库pallet-nfts的使用,支持非同质化代币创建、管理和交易的Substrate模块

以下是基于您提供的NFTs pallet内容的完整示例demo,展示了如何使用NFTs pallet创建集合、铸造NFT、设置元数据并管理交易:

use frame_support::{parameter_types, traits::GenesisBuild};
use sp_core::H256;
use sp_runtime::{
    traits::{BlakeTwo256, IdentityLookup},
    BuildStorage,
};

type Block = frame_system::mocking::MockBlock<Test>;

// 配置测试环境
frame_support::construct_runtime!(
    pub enum Test {
        System: frame_system,
        Balances: pallet_balances,
        Nfts: pallet_nfts,
    }
);

parameter_types! {
    pub const BlockHashCount: u64 = 250;
    pub const SS58Prefix: u8 = 42;
}

impl frame_system::Config for Test {
    type BaseCallFilter = frame_support::traits::Everything;
    type BlockWeights = ();
    type BlockLength = ();
    type DbWeight = ();
    type RuntimeOrigin = RuntimeOrigin;
    type RuntimeCall = RuntimeCall;
    type Index = u64;
    type BlockNumber = u64;
    type Hash = H256;
    type Hashing = BlakeTwo256;
    type AccountId = u64;
    type Lookup = IdentityLookup<Self::AccountId>;
    type Header = sp_runtime::generic::Header<u64, BlakeTwo256>;
    type RuntimeEvent = RuntimeEvent;
    type BlockHashCount = BlockHashCount;
    type Version = ();
    type PalletInfo = PalletInfo;
    type AccountData = pallet_balances::AccountData<u64>;
    type OnNewAccount = ();
    type OnKilledAccount = ();
    type SystemWeightInfo = ();
    type SS58Prefix = SS58Prefix;
    type OnSetCode = ();
    type MaxConsumers = frame_support::traits::ConstU32<16>;
}

parameter_types! {
    pub const ExistentialDeposit: u64 = 1;
    pub const MaxLocks: u32 = 10;
    pub const MaxReserves: u32 = 10;
}

impl pallet_balances::Config for Test {
    type Balance = u64;
    type DustRemoval = ();
    type RuntimeEvent = RuntimeEvent;
    type ExistentialDeposit = ExistentialDeposit;
    type AccountStore = System;
    type WeightInfo = ();
    type MaxLocks = MaxLocks;
    type MaxReserves = MaxReserves;
    type ReserveIdentifier = [u8; 8];
    type HoldIdentifier = ();
    type FreezeIdentifier = ();
    type MaxHolds = ();
    type MaxFreezes = ();
}

parameter_types! {
    pub const CollectionDeposit: u64 = 100;
    pub const ItemDeposit: u64 = 1;
    pub const StringLimit: u32 = 50;
    pub const AttributesLimit: u32 = 10;
    pub const MetadataDepositBase: u64 = 1;
    pub const AttributeDepositBase: u64 = 1;
    pub const DepositPerByte: u64 = 1;
}

impl pallet_nfts::Config for Test {
    type RuntimeEvent = RuntimeEvent;
    type CollectionId = u32;
    type ItemId = u32;
    type Currency = Balances;
    type CreateOrigin = frame_support::traits::AsEnsureOriginWithArg<frame_system::EnsureSigned<u64>>;
    type ForceOrigin = frame_system::EnsureRoot<u64>;
    type CollectionDeposit = CollectionDeposit;
    type ItemDeposit = ItemDeposit;
    type MetadataDepositBase = MetadataDepositBase;
    type AttributeDepositBase = AttributeDepositBase;
    type DepositPerByte = DepositPerByte;
    type StringLimit = StringLimit;
    type KeyLimit = AttributesLimit;
    type ValueLimit = AttributesLimit;
    type WeightInfo = ();
    type Locker = ();
    type RuntimeHoldReason = ();
    type RuntimeFreezeReason = ();
    type FreezeIdentifier = ();
    type MaxFreezes = ();
    type MaxHolds = ();
    type ItemAttributesApprovalsLimit = ();
    type MaxAttributesPerCall = ();
}

// 创建测试环境
pub fn new_test_ext() -> sp_io::TestExternalities {
    let mut storage = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();
    
    pallet_balances::GenesisConfig::<Test> {
        balances: vec![(1, 1000), (2, 1000), (3, 1000), (4, 1000)],
    }
    .assimilate_storage(&mut storage)
    .unwrap();
    
    storage.into()
}

#[test]
fn full_nft_workflow() {
    new_test_ext().execute_with(|| {
        // 1. 创建集合
        assert_ok!(Nfts::create(
            RuntimeOrigin::signed(1),  // 创建者
            1,                        // collection_id
            1,                        // admin
            Default::default()        // 配置
        ));
        
        // 2. 设置集合元数据
        assert_ok!(Nfts::set_collection_metadata(
            RuntimeOrigin::signed(1),
            1,                        // collection_id
            b"My NFT Collection".to_vec(),
        ));
        
        // 3. 铸造NFT
        assert_ok!(Nfts::mint(
            RuntimeOrigin::signed(1),
            1,                        // collection_id
            1,                        // item_id
            1,                        // owner
            Default::default()        // 配置
        ));
        
        // 4. 设置NFT元数据
        assert_ok!(Nfts::set_metadata(
            RuntimeOrigin::signed(1),
            1,                        // collection_id
            1,                        // item_id
            b"My First NFT".to_vec(),
        ));
        
        // 5. 设置NFT属性
        assert_ok!(Nfts::set_attribute(
            RuntimeOrigin::signed(1),
            1,                        // collection_id
            Some(1),                  // item_id (None表示集合属性)
            b"color".to_vec(),
            b"blue".to_vec(),
        ));
        
        // 6. 设置价格并购买
        assert_ok!(Nfts::set_price(
            RuntimeOrigin::signed(1),
            1,                        // collection_id
            1,                        // item_id
            Some(100),                // price
            None                      // 买家白名单
        ));
        
        // 7. 用户2购买NFT
        assert_ok!(Nfts::buy_item(
            RuntimeOrigin::signed(2),
            1,                        // collection_id
            1,                        // item_id
            100                       // bid_price
        ));
        
        // 验证所有权转移
        assert_eq!(Nfts::owner(1, 1), Some(2));
        
        // 8. 创建原子交换
        // 先给用户3铸造一个NFT
        assert_ok!(Nfts::mint(
            RuntimeOrigin::signed(1),
            1,                        // collection_id
            2,                        // item_id
            3,                        // owner
            Default::default()
        ));
        
        // 用户2创建交换报价
        assert_ok!(Nfts::create_swap(
            RuntimeOrigin::signed(2),
            1,                        // offered_collection_id
            1,                        // offered_item_id
            Some(1),                  // desired_collection_id
            Some(2),                  // desired_item_id
            None,                     // price
            None                      // deadline
        ));
        
        // 用户3接受交换
        assert_ok!(Nfts::claim_swap(
            RuntimeOrigin::signed(3),
            vec![(1, 2)],             // sent_items
            2,                        // 发送者
            vec![(1, 1)],             // received_items
        ));
        
        // 验证交换结果
        assert_eq!(Nfts::owner(1, 1), Some(3));  // NFT1现在属于用户3
        assert_eq!(Nfts::owner(1, 2), Some(2));  // NFT2现在属于用户2
        
        // 9. 销毁NFT
        assert_ok!(Nfts::burn(
            RuntimeOrigin::signed(3),
            1,                        // collection_id
            1                         // item_id
        ));
        
        // 验证NFT已销毁
        assert!(!Item::<Test>::contains_key(1, 1));
    });
}

这个完整示例展示了以下NFT工作流程:

  1. 创建NFT集合
  2. 设置集合元数据
  3. 铸造NFT
  4. 设置NFT元数据和属性
  5. 设置价格并完成购买交易
  6. 创建和执行原子交换
  7. 销毁NFT

每个步骤都包含注释说明操作的目的和参数含义。您可以根据实际需求调整或扩展这个示例。


1 回复

Rust区块链NFT开发库pallet-nfts使用指南

完整示例demo

下面是一个完整的pallet-nfts使用示例,包含runtime集成、NFT集合创建、NFT铸造和转移等操作:

// runtime/src/lib.rs

// 1. 引入必要的依赖和配置
pub use pallet_nfts;

impl pallet_nfts::Config for Runtime {
    type RuntimeEvent = RuntimeEvent;
    type CollectionId = u32;  // 使用u32作为集合ID类型
    type ItemId = u32;        // 使用u32作为NFT ID类型
    type Currency = Balances; // 使用Balances作为货币类型
    type CreateOrigin = EnsureSigned<AccountId>; // 只有签名账户可以创建集合
    type ForceOrigin = EnsureRoot<AccountId>;    // 只有root账户可以强制操作
    type Locker = ();         // 不使用锁定功能
    // 其他配置...
}

construct_runtime!(
    pub enum Runtime where
        Block = Block,
        NodeBlock = opaque::Block,
        UncheckedExtrinsic = UncheckedExtrinsic
    {
        // 其他pallet...
        Nfts: pallet_nfts::{Pallet, Call, Storage, Event<T>},
    }
);

// 2. 创建NFT集合的完整示例
fn create_nft_collection() -> DispatchResult {
    let collection_id = 1;
    let admin = frame_system::ensure_signed(origin)?;
    
    pallet_nfts::Pallet::<Runtime>::create(
        RuntimeOrigin::signed(admin.clone()),
        admin,
        pallet_nfts::CollectionConfig {
            settings: pallet_nfts::CollectionSettings::all_enabled(),
            max_supply: None, // 不限制集合大小
            mint_settings: pallet_nfts::MintSettings {
                mint_type: pallet_nfts::MintType::Issuer,
                price: None,
                start_block: None,
                end_block: None,
                default_item_settings: pallet_nfts::ItemSettings::all_enabled(),
            }
        }
    )
}

// 3. 铸造NFT的完整示例
fn mint_nft(collection_id: u32, nft_id: u32, owner: AccountId) -> DispatchResult {
    pallet_nfts::Pallet::<Runtime>::mint(
        RuntimeOrigin::signed(owner.clone()),
        collection_id,
        nft_id,
        owner,
        None // 不需要见证数据
    )
}

// 4. 转移NFT所有权的完整示例
fn transfer_nft(collection_id: u32, nft_id: u32, from: AccountId, to: AccountId) -> DispatchResult {
    pallet_nfts::Pallet::<Runtime>::transfer(
        RuntimeOrigin::signed(from),
        collection_id,
        nft_id,
        to
    )
}

// 5. 设置NFT属性的完整示例
fn set_nft_attribute(collection_id: u32, nft_id: u32, key: Vec<u8>, value: Vec<u8>) -> DispatchResult {
    pallet_nfts::Pallet::<Runtime>::set_attribute(
        RuntimeOrigin::signed(owner),
        collection_id,
        Some(nft_id), // 为特定NFT设置属性
        key,
        value
    )
}

// 6. 设置NFT价格的完整示例
fn set_nft_price(collection_id: u32, nft_id: u32, price: Balance) -> DispatchResult {
    pallet_nfts::Pallet::<Runtime>::set_price(
        RuntimeOrigin::signed(owner),
        collection_id,
        nft_id,
        Some(price), // 设置价格
        None // 不限制买家
    )
}

前端交互完整示例

// 完整的前端交互示例
async function nftDemo() {
    // 1. 创建NFT集合
    const collectionId = 1;
    const adminAddress = '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty';
    
    await api.tx.nfts.create(
        collectionId,
        adminAddress,
        {
            settings: '0b11111111', // 启用所有设置
            maxSupply: null,       // 无限供应
            mintSettings: {
                mintType: 'Issuer',
                price: null,
                startBlock: null,
                endBlock: null,
                defaultItemSettings: '0b11111111'
            }
        }
    ).signAndSend(adminAddress, ({ status }) => {
        if (status.isInBlock) {
            console.log('集合创建成功');
        }
    });
    
    // 2. 铸造NFT
    const nftId = 1;
    const ownerAddress = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
    
    await api.tx.nfts.mint(
        collectionId,
        nftId,
        ownerAddress
    ).signAndSend(adminAddress, ({ status }) => {
        if (status.isInBlock) {
            console.log('NFT铸造成功');
        }
    });
    
    // 3. 设置NFT属性
    await api.tx.nfts.setAttribute(
        collectionId,
        nftId,
        'rarity',
        'legendary'
    ).signAndSend(adminAddress);
    
    // 4. 设置NFT价格
    const price = 1000000000000; // 100个原生代币
    await api.tx.nfts.setPrice(
        collectionId,
        nftId,
        price,
        null
    ).signAndSend(ownerAddress);
    
    // 5. 转移NFT
    const newOwner = '5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y';
    await api.tx.nfts.transfer(
        collectionId,
        nftId,
        newOwner
    ).signAndSend(ownerAddress);
}

注意事项

  1. 确保在runtime中正确配置了pallet-nfts的所有参数
  2. 集合ID和NFT ID必须在链上是唯一的
  3. 考虑存储成本,因为每个NFT都会占用链上存储空间
  4. 合理设置权限控制,避免安全问题
  5. 前端交互时确保使用正确的账户签名

这个完整示例展示了pallet-nfts的主要功能,包括runtime集成、NFT集合创建、NFT铸造、属性设置、定价和转移等操作。

回到顶部