web3.py实现NFT合约全流程

NFT(Non-Fungible Token,非同质化代币)是一种基于区块链技术的数字资产,其核心特点是唯一性、不可分割性和可验证性。以下是详细解释:

1. 定义与核心特性

  • 非同质化:与比特币、以太币等同质化代币(可互换)不同,每个NFT都是独一无二的,无法被其他NFT直接替代。
  • 区块链技术:NFT通常基于以太坊等区块链平台发行,其所有权、交易记录等信息永久存储在区块链上,确保透明且不可篡改。
  • 数字所有权证明:NFT代表对特定数字资产(如图片、音乐、视频、游戏道具等)的所有权,类似数字世界的"房产证"。

2. 常见应用场景

  • 数字艺术品:艺术家将作品转化为NFT,通过拍卖或直接销售获得收益。例如,Beeple的数字画作《Everydays: The First 5000 Days》以6900万美元成交。
  • 收藏品:如加密猫(CryptoKitties)、NBA Top Shot(NBA球星高光时刻NFT)等,用户可收集、交易虚拟物品。
  • 游戏资产:游戏中的角色、装备、土地等可转化为NFT,玩家真正拥有并可跨游戏使用。
  • 音乐与影视:音乐人发行限量版NFT专辑,粉丝购买后获得独家内容或权益。
  • 虚拟世界:在Decentraland等元宇宙平台中,NFT代表虚拟土地、建筑或装饰品。
  • 身份与认证:NFT可用于证明学历、会员资格、票务等,防止伪造。

3. 工作原理

  • 创建(Mint):艺术家或开发者将数字文件上传至区块链平台,生成唯一的NFT,并设定规则(如版税比例)。
  • 交易:NFT可在加密货币交易所或专用市场(如OpenSea)买卖,交易记录公开可查。
  • 所有权转移:购买者通过加密钱包接收NFT,成为其唯一所有者,可转售或展示。

4. 优势与争议

  • 优势
    • 确权与溯源:解决数字内容复制难题,保护创作者权益。
    • 增值潜力:稀缺性可能推动NFT价值上涨,吸引投资者。
    • 创新商业模式:创作者可通过版税持续获得收益(如每次转售抽取一定比例)。
  • 争议
    • 环境问题:部分区块链(如以太坊)能耗高,引发环保质疑。
    • 市场泡沫:部分NFT价格虚高,存在投机炒作风险。
    • 版权纠纷:NFT化可能涉及未经授权使用他人作品。

5. 简单类比

想象你拥有一幅名画,但它是数字形式的(如一张JPEG图片)。通过NFT技术,你可以:

  • 证明这是"原版"而非复制品;
  • 在区块链上记录你拥有它;
  • 自由出售或展示,甚至设置未来每次转售时你都能获得分成。

总结

NFT是数字世界中的"唯一凭证",它重新定义了数字资产的所有权与交易方式,为创作者、收藏家和投资者提供了新机遇,但同时也需警惕市场风险与伦理问题。

NFT 核心架构图解

在开始代码之前,我们需要建立正确的心理模型。NFT 的本质不仅仅是一张图片,它是一个 "指向数据的权属证明"

  • Token ID : 链上唯一的标识符(如 #1024)。
  • Owner: 链上记录的持有者地址。
  • Metadata (元数据): 包含图片、名称、属性的 JSON 文件,通常存储在 IPFS 上。
  • TokenURI: 智能合约中存储的一个字符串,指向 Metadata 的位置。

阶段一:元数据 (Metadata) ------ NFT 的灵魂

在铸造(Mint)之前,你需要准备好 NFT 的内容。为了去中心化,我们通常不把图片放在中心化服务器(AWS/阿里云),而是放在 IPFS

1. 标准 Metadata 格式 (JSON)

这是 OpenSea 和主流市场通用的标准:

json 复制代码
{
  "name": "CyberPunk #2077",
  "description": "这是一个来自未来的赛博朋克战士。",
  "image": "ipfs://QmYourImageHash...", 
  "attributes": [
    { "trait_type": "Background", "value": "Neon City" },
    { "trait_type": "Weapon", "value": "Katana" },
    { "trait_type": "Level", "value": 5 }
  ]
}

💡 关键点:合约里存的只是上面这个 JSON 文件的链接(TokenURI),而不是 JSON 内容本身。


阶段二:Mint (铸造) ------ 资产诞生

铸造是将数据写入区块链的过程。我们将使用 Solidity 和 OpenZeppelin 库(行业安全标准)。

Solidity 合约示例

solidity 复制代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract ProNFT is ERC721URIStorage, Ownable {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    constructor() ERC721("ProfessionalNFT", "PNFT") {}

    // 铸造函数
    function mintNFT(address recipient, string memory tokenURI)
        public
        onlyOwner
        returns (uint256)
    {
        _tokenIds.increment();
        uint256 newItemId = _tokenIds.current();

        // 1. 铸造给指定地址
        _safeMint(recipient, newItemId);
        
        // 2. 设置元数据链接
        _setTokenURI(newItemId, tokenURI);

        return newItemId;
    }
}

阶段三:Approve (授权) ------ 交易的前提

这是新手最容易困惑的地方。
为什么卖 NFT 时需要先"授权"?

因为智能合约(如 OpenSea 的合约)不能直接拿走你钱包里的 NFT。你必须先执行 approve 操作,告诉 NFT 合约:"我允许 OpenSea 支配我的这个 Token"。

两种授权方式:

  1. 单个授权 (approve): 只允许操作特定的 Token ID。
  2. 全员授权 (setApprovalForAll): 允许操作该系列下你拥有的所有 NFT(省 Gas,OpenSea 常用此法)。

Solidity 接口定义

solidity 复制代码
// 允许 operator 地址挪动你的 tokenId
function approve(address operator, uint256 tokenId) external;

// 允许 operator 挪动你所有的 NFT
function setApprovalForAll(address operator, bool _approved) external;

阶段四:Transfer (转移) ------ 所有权变更

转移有两种方式,但在开发中强烈建议 只使用 safeTransferFrom

  • transferFrom: 暴力转账。如果接收方是一个无法处理 NFT 的合约,NFT 可能会永久丢失。
  • safeTransferFrom: 安全转账。它会检查接收方是否实现了 onERC721Received 接口,防止资产锁死。

Python (Web3.py) 调用示例:

python 复制代码
# 假设这是从 Alice 转给 Bob
tx = contract.functions.safeTransferFrom(
    alice_address, # From
    bob_address,   # To
    1              # Token ID
).transact({"from": alice_address})

阶段五:Marketplace (交易) ------ 买卖逻辑

NFT 本身没有"价格"属性。交易功能是由市场合约(Marketplace Contract)实现的。

简易市场交易逻辑(原子化交易):

  1. Listing (挂单): 卖家授权市场合约,并设定价格(存储在市场合约的数据结构中)。
  2. Buying (购买) : 买家调用市场合约的 buy 函数并附带 ETH。
  3. Settlement (结算) : 市场合约同时做两件事:
    • 把 ETH 转给卖家。
    • 把 NFT 从卖家转给买家(利用之前的授权)。

核心 Solidity 代码片段

solidity 复制代码
// 这是一个简化的市场购买函数
function buyNFT(address nftContract, uint256 tokenId) external payable {
    // 1. 获取上架信息
    Listing memory item = listings[nftContract][tokenId];
    require(item.price > 0, "Not for sale");
    require(msg.value >= item.price, "Insufficient funds");

    // 2. 结算资金 (买家 -> 卖家)
    payable(item.seller).transfer(item.price);

    // 3. 转移 NFT (卖家 -> 买家)
    // 注意:这里市场合约调用 NFT 合约,将 NFT 从 Seller 账户拉取并给 Buyer
    IERC721(nftContract).safeTransferFrom(item.seller, msg.sender, tokenId);
    
    // 4. 清除上架状态
    delete listings[nftContract][tokenId];
}

阶段六:Swap (交换) ------ 以物易物

Swap 是指 NFT 换 NFT,或者 NFT 换 ERC20 Token。去中心化交换的核心是互信 。我们不需要互信,我们需要一个Swap 合约作为中介。

流程:

  1. User A 授权 Swap 合约操作 NFT A。
  2. User B 授权 Swap 合约操作 NFT B。
  3. 任意一方调用 swap 函数,合约同时执行两个 transferFrom
solidity 复制代码
function swap(
    address nftA_Contract, uint256 idA, address ownerA,
    address nftB_Contract, uint256 idB, address ownerB
) external {
    // 这是一个原子操作,要么全成功,要么全失败
    IERC721(nftA_Contract).transferFrom(ownerA, ownerB, idA);
    IERC721(nftB_Contract).transferFrom(ownerB, ownerA, idB);
}

实战演练:完整的 Python 交互脚本

先部署一个 MyNFT 合约。使用 Python (web3.py) 走完铸造 -> 授权 -> 转移全流程的代码。

1. 环境准备

你需要安装 web3 库:
pip install web3

python 复制代码
from web3 import Web3

# 1. 连接区块链 (这里使用本地test节点)
w3 = Web3(Web3.EthereumTesterProvider())

if not w3.is_connected():
    raise Exception("无法连接到区块链节点")

# 模拟的三个用户地址 (使用测试节点的前三个账户)
admin = w3.eth.accounts[0] # 铸造者
alice = w3.eth.accounts[1] # 主要持有人
bob = w3.eth.accounts[2]   # 市场/操作员

还需要一个NFT合约,代码如下:

solidity 复制代码
// SPDX-License-Identifier: MIT
// Compatible with OpenZeppelin Contracts ^5.5.0
pragma solidity ^0.8.27;

import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {ERC721Burnable} from "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol";
import {ERC721URIStorage} from "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

contract MyNFT is ERC721, ERC721URIStorage, ERC721Burnable, Ownable {
    uint256 private _nextTokenId;

    constructor(address initialOwner)
        ERC721("MyNFT", "MT")
        Ownable(initialOwner)
    {}

    function mint(address to, string memory uri)
        public
        onlyOwner
        returns (uint256)
    {
        uint256 tokenId = _nextTokenId++;
        _safeMint(to, tokenId);
        _setTokenURI(tokenId, uri);
        return tokenId;
    }

    // The following functions are overrides required by Solidity.

    function tokenURI(uint256 tokenId)
        public
        view
        override(ERC721, ERC721URIStorage)
        returns (string memory)
    {
        return super.tokenURI(tokenId);
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721, ERC721URIStorage)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
}
python 复制代码
# 先部署合约,然后获取合约地址和ABI
from solcx import install_solc, set_solc_version
from solcx import compile_files

install_solc(version='0.8.30')
set_solc_version('0.8.30')

compiled_sol = compile_files([
    "contracts/MyNFT.sol"
], import_remappings=["@openzeppelin=openzeppelin-contracts"])
contract_id, contract_interface = compiled_sol.popitem()
# get bytecode / bin
bytecode = contract_interface['bin']
# get abi
abi = contract_interface['abi']

MyNFTContract = w3.eth.contract(abi=abi, bytecode=bytecode)
tx_hash = MyNFTContract.constructor(admin).transact({'from': admin})
tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
nft_contract = w3.eth.contract(address=tx_receipt.contractAddress, abi=abi)
python 复制代码
import base64
import json

print("\n--- NFT ERC721 全流程模拟开始 ---")
print(f"Admin (铸造者): {admin}")
print(f"Alice (持有人):  {alice}")
print(f"Bob (操作员):    {bob}\n")

# ----------------------------------------------------
# 辅助函数
# ----------------------------------------------------

def encode_metadata_json(metadata_dict):
    """将 NFT Metadata 转成 base64 data URI"""
    json_str = json.dumps(metadata_dict)
    encoded = base64.b64encode(json_str.encode()).decode()
    return f"data:application/json;base64,{encoded}"

def transact_and_wait(func, from_addr):
    """发送交易并等待确认"""
    tx_hash = func.transact({"from": from_addr})
    return w3.eth.wait_for_transaction_receipt(tx_hash)

def check_status(step_name, token_id=None):
    """打印当前状态的辅助函数"""
    print(f"\n--- {step_name} ---")
    print(f"Alice Balance: {nft_contract.functions.balanceOf(alice).call()}")
    print(f"Bob Balance:   {nft_contract.functions.balanceOf(bob).call()}")

    if token_id is not None:
        try:
            owner = nft_contract.functions.ownerOf(token_id).call()
            uri = nft_contract.functions.tokenURI(token_id).call()
            approved = nft_contract.functions.getApproved(token_id).call()
            print(f"Token {token_id} Owner:  {owner}")
            print(f"Token {token_id} URI:    {uri}")
            print(f"Token {token_id} Approved: {approved}")
        except:
            print(f"Token {token_id} 不存在或已被销毁。")

    print(f"Alice 授权 Bob: {nft_contract.functions.isApprovedForAll(alice, bob).call()}")

# ----------------------------------------------------
# 步骤 1: 检查 name / symbol
# ----------------------------------------------------
print("name:", nft_contract.functions.name().call())
print("symbol:", nft_contract.functions.symbol().call())

# ----------------------------------------------------
# 步骤 2: Admin 铸造 Token 1 给 Alice
# ----------------------------------------------------
nft_metadata = {
    "name": "CyberPunk #2077",
    "description": "来自未来的赛博朋克战士装备。",
    "image": "ipfs://QmImageHash...",
    "attributes": [
        {"trait_type": "Background", "value": "Neon City"},
        {"trait_type": "Weapon",     "value": "Katana"},
        {"trait_type": "Level",      "value": 5}
    ]
}

token_uri = encode_metadata_json(nft_metadata)

transact_and_wait(
    nft_contract.functions.mint(alice, token_uri),
    admin
)
check_status("2. Admin 铸造 Token ID 1 给 Alice", 0)  # tokenId = 0 (从 0 开始)

# ----------------------------------------------------
# 步骤 3: Alice 授权 Bob 操作 Token ID 0
# ----------------------------------------------------
transact_and_wait(
    nft_contract.functions.approve(bob, 0),
    alice
)
check_status("3. Alice 授权 Bob 操作 Token 0", 0)

# ----------------------------------------------------
# 步骤 4: Bob 转移 Token 0 给自己
# ----------------------------------------------------
transact_and_wait(
    nft_contract.functions.transferFrom(alice, bob, 0),
    bob
)
check_status("4. Bob 将 Token 0 转给自己", 0)

# ----------------------------------------------------
# 步骤 5: Bob 授权 Admin 为全局操作员
# ----------------------------------------------------
transact_and_wait(
    nft_contract.functions.setApprovalForAll(admin, True),
    bob
)
check_status("5. Bob 设置 Admin 为全局 Operator", 0)

# ----------------------------------------------------
# 步骤 6: Admin 再铸造 Token 1 给 Bob
# ----------------------------------------------------
transact_and_wait(
    nft_contract.functions.mint(bob, token_uri),
    admin
)
check_status("6. Admin 铸造 Token 1 给 Bob", 1)

# ----------------------------------------------------
# 步骤 7: Admin (operator) 安全转移 Token 1 给 Alice
# ----------------------------------------------------
transact_and_wait(
    nft_contract.functions.safeTransferFrom(bob, alice, 1),
    admin
)
check_status("7. Operator Admin 将 Token 1 转给 Alice", 1)

# ----------------------------------------------------
# 步骤 8: Alice 销毁 Token 1
# ----------------------------------------------------
transact_and_wait(
    nft_contract.functions.burn(1),
    alice
)
check_status("8. Alice 销毁 Token 1")

# ----------------------------------------------------
# 步骤 9: Bob 撤销 Admin 的操作员权限
# ----------------------------------------------------
transact_and_wait(
    nft_contract.functions.setApprovalForAll(admin, False),
    bob
)
check_status("9. Bob 撤销 Admin 的 Operator 权限", 0)

print("\n--- 完整 NFT 流程测试结束 ---")
复制代码
--- NFT ERC721 全流程模拟开始 ---
Admin (铸造者): 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf
Alice (持有人):  0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF
Bob (操作员):    0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69

name: MyNFT
symbol: MT

--- 2. Admin 铸造 Token ID 1 给 Alice ---
Alice Balance: 1
Bob Balance:   0
Token 0 Owner:  0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF
Token 0 URI:    data:application/json;base64,eyJuYW1lIjogIkN5YmVyUHVuayAjMjA3NyIsICJkZXNjcmlwdGlvbiI6ICJcdTY3NjVcdTgxZWFcdTY3MmFcdTY3NjVcdTc2ODRcdThkNWJcdTUzNWFcdTY3MGJcdTUxNGJcdTYyMThcdTU4ZWJcdTg4YzVcdTU5MDdcdTMwMDIiLCAiaW1hZ2UiOiAiaXBmczovL1FtSW1hZ2VIYXNoLi4uIiwgImF0dHJpYnV0ZXMiOiBbeyJ0cmFpdF90eXBlIjogIkJhY2tncm91bmQiLCAidmFsdWUiOiAiTmVvbiBDaXR5In0sIHsidHJhaXRfdHlwZSI6ICJXZWFwb24iLCAidmFsdWUiOiAiS2F0YW5hIn0sIHsidHJhaXRfdHlwZSI6ICJMZXZlbCIsICJ2YWx1ZSI6IDV9XX0=
Token 0 Approved: 0x0000000000000000000000000000000000000000
Alice 授权 Bob: False

--- 3. Alice 授权 Bob 操作 Token 0 ---
Alice Balance: 1
Bob Balance:   0
Token 0 Owner:  0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF
Token 0 URI:    data:application/json;base64,eyJuYW1lIjogIkN5YmVyUHVuayAjMjA3NyIsICJkZXNjcmlwdGlvbiI6ICJcdTY3NjVcdTgxZWFcdTY3MmFcdTY3NjVcdTc2ODRcdThkNWJcdTUzNWFcdTY3MGJcdTUxNGJcdTYyMThcdTU4ZWJcdTg4YzVcdTU5MDdcdTMwMDIiLCAiaW1hZ2UiOiAiaXBmczovL1FtSW1hZ2VIYXNoLi4uIiwgImF0dHJpYnV0ZXMiOiBbeyJ0cmFpdF90eXBlIjogIkJhY2tncm91bmQiLCAidmFsdWUiOiAiTmVvbiBDaXR5In0sIHsidHJhaXRfdHlwZSI6ICJXZWFwb24iLCAidmFsdWUiOiAiS2F0YW5hIn0sIHsidHJhaXRfdHlwZSI6ICJMZXZlbCIsICJ2YWx1ZSI6IDV9XX0=
Token 0 Approved: 0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69
Alice 授权 Bob: False

--- 4. Bob 将 Token 0 转给自己 ---
Alice Balance: 0
Bob Balance:   1
Token 0 Owner:  0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69
Token 0 URI:    data:application/json;base64,eyJuYW1lIjogIkN5YmVyUHVuayAjMjA3NyIsICJkZXNjcmlwdGlvbiI6ICJcdTY3NjVcdTgxZWFcdTY3MmFcdTY3NjVcdTc2ODRcdThkNWJcdTUzNWFcdTY3MGJcdTUxNGJcdTYyMThcdTU4ZWJcdTg4YzVcdTU5MDdcdTMwMDIiLCAiaW1hZ2UiOiAiaXBmczovL1FtSW1hZ2VIYXNoLi4uIiwgImF0dHJpYnV0ZXMiOiBbeyJ0cmFpdF90eXBlIjogIkJhY2tncm91bmQiLCAidmFsdWUiOiAiTmVvbiBDaXR5In0sIHsidHJhaXRfdHlwZSI6ICJXZWFwb24iLCAidmFsdWUiOiAiS2F0YW5hIn0sIHsidHJhaXRfdHlwZSI6ICJMZXZlbCIsICJ2YWx1ZSI6IDV9XX0=
Token 0 Approved: 0x0000000000000000000000000000000000000000
Alice 授权 Bob: False

--- 5. Bob 设置 Admin 为全局 Operator ---
Alice Balance: 0
Bob Balance:   1
Token 0 Owner:  0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69
Token 0 URI:    data:application/json;base64,eyJuYW1lIjogIkN5YmVyUHVuayAjMjA3NyIsICJkZXNjcmlwdGlvbiI6ICJcdTY3NjVcdTgxZWFcdTY3MmFcdTY3NjVcdTc2ODRcdThkNWJcdTUzNWFcdTY3MGJcdTUxNGJcdTYyMThcdTU4ZWJcdTg4YzVcdTU5MDdcdTMwMDIiLCAiaW1hZ2UiOiAiaXBmczovL1FtSW1hZ2VIYXNoLi4uIiwgImF0dHJpYnV0ZXMiOiBbeyJ0cmFpdF90eXBlIjogIkJhY2tncm91bmQiLCAidmFsdWUiOiAiTmVvbiBDaXR5In0sIHsidHJhaXRfdHlwZSI6ICJXZWFwb24iLCAidmFsdWUiOiAiS2F0YW5hIn0sIHsidHJhaXRfdHlwZSI6ICJMZXZlbCIsICJ2YWx1ZSI6IDV9XX0=
Token 0 Approved: 0x0000000000000000000000000000000000000000
Alice 授权 Bob: False

--- 6. Admin 铸造 Token 1 给 Bob ---
Alice Balance: 0
Bob Balance:   2
Token 1 Owner:  0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69
Token 1 URI:    data:application/json;base64,eyJuYW1lIjogIkN5YmVyUHVuayAjMjA3NyIsICJkZXNjcmlwdGlvbiI6ICJcdTY3NjVcdTgxZWFcdTY3MmFcdTY3NjVcdTc2ODRcdThkNWJcdTUzNWFcdTY3MGJcdTUxNGJcdTYyMThcdTU4ZWJcdTg4YzVcdTU5MDdcdTMwMDIiLCAiaW1hZ2UiOiAiaXBmczovL1FtSW1hZ2VIYXNoLi4uIiwgImF0dHJpYnV0ZXMiOiBbeyJ0cmFpdF90eXBlIjogIkJhY2tncm91bmQiLCAidmFsdWUiOiAiTmVvbiBDaXR5In0sIHsidHJhaXRfdHlwZSI6ICJXZWFwb24iLCAidmFsdWUiOiAiS2F0YW5hIn0sIHsidHJhaXRfdHlwZSI6ICJMZXZlbCIsICJ2YWx1ZSI6IDV9XX0=
Token 1 Approved: 0x0000000000000000000000000000000000000000
Alice 授权 Bob: False

--- 7. Operator Admin 将 Token 1 转给 Alice ---
Alice Balance: 1
Bob Balance:   1
Token 1 Owner:  0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF
Token 1 URI:    data:application/json;base64,eyJuYW1lIjogIkN5YmVyUHVuayAjMjA3NyIsICJkZXNjcmlwdGlvbiI6ICJcdTY3NjVcdTgxZWFcdTY3MmFcdTY3NjVcdTc2ODRcdThkNWJcdTUzNWFcdTY3MGJcdTUxNGJcdTYyMThcdTU4ZWJcdTg4YzVcdTU5MDdcdTMwMDIiLCAiaW1hZ2UiOiAiaXBmczovL1FtSW1hZ2VIYXNoLi4uIiwgImF0dHJpYnV0ZXMiOiBbeyJ0cmFpdF90eXBlIjogIkJhY2tncm91bmQiLCAidmFsdWUiOiAiTmVvbiBDaXR5In0sIHsidHJhaXRfdHlwZSI6ICJXZWFwb24iLCAidmFsdWUiOiAiS2F0YW5hIn0sIHsidHJhaXRfdHlwZSI6ICJMZXZlbCIsICJ2YWx1ZSI6IDV9XX0=
Token 1 Approved: 0x0000000000000000000000000000000000000000
Alice 授权 Bob: False

--- 8. Alice 销毁 Token 1 ---
Alice Balance: 0
Bob Balance:   1
Alice 授权 Bob: False

--- 9. Bob 撤销 Admin 的 Operator 权限 ---
Alice Balance: 0
Bob Balance:   1
Token 0 Owner:  0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69
Token 0 URI:    data:application/json;base64,eyJuYW1lIjogIkN5YmVyUHVuayAjMjA3NyIsICJkZXNjcmlwdGlvbiI6ICJcdTY3NjVcdTgxZWFcdTY3MmFcdTY3NjVcdTc2ODRcdThkNWJcdTUzNWFcdTY3MGJcdTUxNGJcdTYyMThcdTU4ZWJcdTg4YzVcdTU5MDdcdTMwMDIiLCAiaW1hZ2UiOiAiaXBmczovL1FtSW1hZ2VIYXNoLi4uIiwgImF0dHJpYnV0ZXMiOiBbeyJ0cmFpdF90eXBlIjogIkJhY2tncm91bmQiLCAidmFsdWUiOiAiTmVvbiBDaXR5In0sIHsidHJhaXRfdHlwZSI6ICJXZWFwb24iLCAidmFsdWUiOiAiS2F0YW5hIn0sIHsidHJhaXRfdHlwZSI6ICJMZXZlbCIsICJ2YWx1ZSI6IDV9XX0=
Token 0 Approved: 0x0000000000000000000000000000000000000000
Alice 授权 Bob: False

--- 完整 NFT 流程测试结束 ---

你现在已经有了一个完整的 NFT 交易流程

包含:

步骤 功能
1 查看 name / symbol
2 mint(内置 JSON metadata → base64)
3 approve(单授权)
4 transferFrom(被授权者转移)
5 setApprovalForAll(市场授权)
6 再次 mint
7 safeTransferFrom(操作员转移)
8 burn(销毁)
9 revoke operator(撤销授权)

补充:meta data通常是用 IPFS(nft.storage / pinata)或去中心化方案进行存储的,而不是直接用 base64 存在链上,这里为了简化演示才这么做的。IPFS介绍可以参考另一篇博客

相关推荐
myan3 小时前
RWA 将改变华尔街——Uweb纽约游学总结(下)
区块链
九河云3 小时前
血液中心 “冷链箱 IoT + 区块链”:让每一袋血浆的 2-8℃曲线被法院采证,断链纠纷降为 0
物联网·区块链
Rockbean4 小时前
3分钟Solidity: 5.2 发送以太币(传输、发送、调用)
web3·区块链·solidity
IvorySQL8 小时前
活动回顾|Oracle 到 PostgreSQL 迁移技术网络研讨会
postgresql·oracle·区块链
怪只怪满眼尽是人间烟火8 小时前
离线环境下部署区块链FISCO BCOS v2.11.0
linux·运维·区块链
古城小栈8 小时前
AI + 区块链:去中心化智能的未来形态
人工智能·去中心化·区块链
Wnq100729 小时前
去中心化分布式计算与云计算的性能对比研究
云计算·去中心化·区块链
Biteagle1 天前
Movement 新公链机制解析:下一代区块链的创新与突破
区块链
TechubNews1 天前
Aave V4:从割裂市场到模块化流动性
区块链