用 Python 制作 NFT 区块链作品(下)

Python中文社区

共 5183字,需浏览 11分钟

 · 2021-06-20

在本文中,我们将学习如何使用 Brownie、Python 和 Chainlink 来制作非同质化的 NFT 作品,并在 OpenSea NFT 市场上展示和销售我们的成果。在学习本文前,请阅读用 Python 制作 NFT 区块链作品(上)

动态和高级 NFT

动态 NFT 是可以随时间变化的 NFT,或者具有我们可以用来相互交互的链上功能。这些 NFT 可以无限定制,让我们可以制作整个游戏、元宇宙(metaverse)或某种互动艺术。下面我们进入高级部分。

高级快速入门

确保您的metamask中有足够的测试网 ETH 和 LINK,然后运行以下命令:

  1. brownie run scripts/advanced_collectible/deploy_advanced.py --network rinkeby

  2. brownie run scripts/advanced_collectible/create_collectible.py --network rinkeby

我们的收藏品是从 Chainlink VRF 返回的随机犬种。Chainlink VRF 是一种获得可证明随机数的方法,因此我们的 NFT 真正稀缺。然后我们想要创建它的元数据。

  1. brownie run scripts/advanced_collectible/create_metadata.py --network rinkeby

然后我们可以选择将此数据上传到 IPFS,以便我们可以拥有一个 tokenURI。稍后我会告诉你如何做到这一点。现在,我们将仅使用以下示例 tokenURI

  1. https://ipfs.io/ipfs/Qmd9MCGtdVz2miNumBHDbvj8bigSgTwnr4SbyH6DNnpWdt?filename=1-PUG.json

如果您将 IPFS Companion 下载到您的浏览器中,您可以使用该 URL 来查看 URI 返回的内容。它看起来像这样:

  1. {

  2. "name": "PUG",

  3. "description": "An adorable PUG pup!",

  4. "image": "https://ipfs.io/ipfs/QmSsYRx3LpDAb1GZQm7zZ1AuHZjfbPkD6J7s9r41xu1mf8?filename=pug.png",

  5. "attributes": [

  6. {

  7. "trait_type": "cuteness",

  8. "value": 100

  9. }

  10. ]

  11. }

然后我们可以运行我们的 set_tokenuri.py 脚本:

  1. brownie run scripts/advanced_collectible/set_tokenuri.py --network rinkeby

我们会得到这样的输出:

  1. Running 'scripts/advanced_collectible/set_tokenuri.py::main'...

  2. Working on rinkeby

  3. Transaction sent: 0x8a83a446c306d6255952880c0ca35fa420248a84ba7484c3798d8bbad421f88e

  4. Gas price: 1.0 gwei Gas limit: 44601 Nonce: 354

  5. AdvancedCollectible.setTokenURI confirmed - Block: 8331653 Gas used: 40547 (90.91%)


  6. Awesome! You can view your NFT at https://testnets.opensea.io/assets/0x679c5f9adC630663a6e63Fa27153B215fe021b34/0

  7. Please give up to 20 minutes, and hit the "refresh metadata" button

我们可以点击给出的链接,看看它在 Opensea 上的样子!您可能需要点击刷新元数据按钮并等待几分钟。

随机品种

然我们看一下刚刚做了什么。这是我们的 AdvancedCollectible.sol

  1. pragma solidity 0.6.6;


  2. import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

  3. import "@chainlink/contracts/src/v0.6/VRFConsumerBase.sol";


  4. contract AdvancedCollectible is ERC721, VRFConsumerBase {

  5. uint256 public tokenCounter;

  6. enum Breed{PUG, SHIBA_INU, BRENARD}

  7. // add other things

  8. mapping(bytes32 => address) public requestIdToSender;

  9. mapping(bytes32 => string) public requestIdToTokenURI;

  10. mapping(uint256 => Breed) public tokenIdToBreed;

  11. mapping(bytes32 => uint256) public requestIdToTokenId;

  12. event requestedCollectible(bytes32 indexed requestId);



  13. bytes32 internal keyHash;

  14. uint256 internal fee;

  15. uint256 public randomResult;

  16. constructor(address _VRFCoordinator, address _LinkToken, bytes32 _keyhash)

  17. public

  18. VRFConsumerBase(_VRFCoordinator, _LinkToken)

  19. ERC721("Dogie", "DOG")

  20. {

  21. tokenCounter = 0;

  22. keyHash = _keyhash;

  23. fee = 0.1 * 10 ** 18;

  24. }


  25. function createCollectible(string memory tokenURI, uint256 userProvidedSeed)

  26. public returns (bytes32){

  27. bytes32 requestId = requestRandomness(keyHash, fee, userProvidedSeed);

  28. requestIdToSender[requestId] = msg.sender;

  29. requestIdToTokenURI[requestId] = tokenURI;

  30. emit requestedCollectible(requestId);

  31. }


  32. function fulfillRandomness(bytes32 requestId, uint256 randomNumber) internal override {

  33. address dogOwner = requestIdToSender[requestId];

  34. string memory tokenURI = requestIdToTokenURI[requestId];

  35. uint256 newItemId = tokenCounter;

  36. _safeMint(dogOwner, newItemId);

  37. _setTokenURI(newItemId, tokenURI);

  38. Breed breed = Breed(randomNumber % 3);

  39. tokenIdToBreed[newItemId] = breed;

  40. requestIdToTokenId[requestId] = newItemId;

  41. tokenCounter = tokenCounter + 1;

  42. }


  43. function setTokenURI(uint256 tokenId, string memory _tokenURI) public {

  44. require(

  45. _isApprovedOrOwner(_msgSender(), tokenId),

  46. "ERC721: transfer caller is not owner nor approved"

  47. );

  48. _setTokenURI(tokenId, _tokenURI);

  49. }

  50. }

我们使用 Chainlink VRF 从 PUG、SHIBA_INU、BRENARD 列表中创建一个随机品种。当我们这次调用 createCollectible 时,我们实际上向链下的 Chainlink VRF 节点发起了一个请求,并返回一个随机数,以使用这 3 个品种之一创建 NFT。

在你的 NFT 中使用真正的随机性是创造真正稀缺性的好方法,使用 Chainlink oracle 随机数意味着你的数字可以证明是随机的,并且不会受到矿工的影响。

您可以在文档中了解有关 Chainlink VRF 的更多信息。

https://docs.chain.link/docs/chainlink-vrf/

Chainlink 节点通过调用 fulfillRandomness 函数进行响应,并根据随机数创建收藏品。然后我们仍然需要调用 _setTokenURI 来为我们的 NFT 提供它需要的外观。

我们没有在这里给出我们的 NFT 属性,但属性是让我们的 NFT 进行交互的好方法。您可以在此 龙与地下城示例中看到具有属性的 NFT 的一个很好的示例。

https://github.com/PatrickAlphaC/dungeons-and-dragons-nft

来自 IPFS 的元数据

我们使用 IPFS 来存储两个文件:

  • NFT 的形象(哈巴狗形象)

  • tokenURI 文件(JSON 文件,其中还包含图像的链接)

我们使用 IPFS 是因为它是一个免费的去中心化平台。我们可以通过下载 IPFS 桌面并点击导入按钮将我们的 tokenURI 和图像添加到 IPFS。

然后,我们可以通过点击要共享的文件旁边的 3 个点、点击共享链接并复制给定的链接来共享 URI。然后我们可以将此链接添加到我们的 set_tokenuri.py 文件中以更改我们想要使用的 tokenURI

持久性

但是,如果 tokenURI 仅在我们的节点上,这意味着当我们的节点关闭时,没有其他人可以查看它。所以我们希望其他人 pin我们的 NFT。我们可以使用 Pinata 之类的 pin服务来帮助我们的数据保持活动状态,即使我们的 IPFS 节点已关闭。

我想未来会有越来越多的元数据存储在 IPFS 和去中心化存储平台上。集中式服务器可能会宕机,这意味着这些 NFT 上的艺术将永远丢失。请务必检查您使用的 NFT 的 tokenURI 所在的位置!

我也希望更多的人会使用像 Filecoin 这样的 dStorage 平台,因为使用 pin服务也没有像它应该的那样去中心化。

现在,您已经具备了制作漂亮有趣、可定制、交互式 NFT 的技能,并让它们在市场上呈现。

NFT 是一种有趣、强大的方式,可以补偿艺术家们所做的辛勤工作。


欢迎添加下方二维码加入社群

一起探讨Python与区块链开发技术




点击下方阅读原文加入社区会员

浏览 23
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报