如何使用 Ethers 铸造 NFT
原文:https://www.web3.university/tracks/build-your-first-nft/how-to-mint-an-nft-using-ethers-js
本教程描述了如何通过 ethers.js 库使用 Ethers 在以太坊区块链上铸造 NFT,以及我们在第一部分中的智能契约:如何创建 NFT。我们还将探索基本的测试。
完成本指南的预计时间:~10 分钟
在本练习中,我们将带您完成一个替代实现,使用版本 4 的 OpenZeppelin 库以及 Ethers.js 以太坊库来代替 Web3。
我们还将介绍用 Hardhat 和 Waffle 测试你的合同的基础知识。在这个教程中,我使用的是 Yarn,但是如果你愿意,你也可以使用 npm/npx。
最后,我们将使用 TypeScript。这是相当有据可查的,所以我们不会在这里覆盖它。
在所有其他方面,本教程的工作方式与 Web3 版本相同,包括 Pinata 和 IPFS 等工具。
快速提醒
提醒一下,“铸造 NFT”是在区块链上发布您的 ERC721 令牌的唯一实例的行为。本教程假设您已经在 NFT 教程系列的第一部分中成功地部署了一个智能合约到 Goerli 网络,其中包括。
第一步:创建你的可靠性合同
OpenZeppelin 是用于安全智能契约开发的库。您只需继承它们的流行标准(如 ERC20 或 ERC721)的实现,并根据您的需要扩展其行为。我们将把这个文件放在 contracts/MyNFT.sol。
-- CODE language-js line-numbers --
// Contract based on https://docs.openzeppelin.com/contracts/4.x/erc721
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
contract MyNFT is ERC721URIStorage {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
constructor() ERC721("MyNFT", "MNFT") {}
function mintNFT(address recipient, string memory tokenURI)
public
returns (uint256)
{
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
_mint(recipient, newItemId);
_setTokenURI(newItemId, tokenURI);
return newItemId;
}
}
步骤 2:创建安全帽任务来部署我们的合同和铸造 NFT 的
创建包含以下内容的文件 tasks/nft.ts:
-- CODE language-js line-numbers --
import { task, types } from "hardhat/config";
import { Contract } from "ethers";
import { TransactionResponse } from "@ethersproject/abstract-provider";
import { env } from "../lib/env";
import { getContract } from "../lib/contract";
import { getWallet } from "../lib/wallet";
task("deploy-contract", "Deploy NFT contract").setAction(async (_, hre) => {
return hre.ethers
.getContractFactory("MyNFT", getWallet())
.then((contractFactory) => contractFactory.deploy())
.then((result) => {
process.stdout.write(`Contract address: ${result.address}`);
});
});
task("mint-nft", "Mint an NFT")
.addParam("tokenUri", "Your ERC721 Token URI", undefined, types.string)
.setAction(async (tokenUri, hre) => {
return getContract("MyNFT", hre)
.then((contract: Contract) => {
return contract.mintNFT(env("ETH_PUBLIC_KEY"), tokenUri, {
gasLimit: 500_000,
});
})
.then((tr: TransactionResponse) => {
process.stdout.write(`TX hash: ${tr.hash}`);
});
});
步骤 3:创建助手
您会注意到我们的任务导入了一些助手。他们来了。
合同条款
env.ts
-- CODE language-js line-numbers --
export function env(key: string): string {
const value = process.env[key];
if (value === undefined) {
throw `${key} is undefined`;
}
return value;
}
provider.ts
注意,最后一个 getProvider()函数使用了 ropsten 网络。该参数是可选的,如果省略,则默认为“homestead”。我们当然在使用炼金术,但是有几个支持的替代方案。
-- CODE language-js line-numbers --
import { ethers } from "ethers";
export function getProvider(): ethers.providers.Provider {
return ethers.getDefaultProvider("ropsten", {
alchemy: process.env.ALCHEMY_API_KEY,
});
}
wallet.ts
-- CODE language-js line-numbers --
import { ethers } from "ethers";
import { env } from "./env";
import { getProvider } from "./provider";
export function getWallet(): ethers.Wallet {
return new ethers.Wallet(env("ETH_PRIVATE_KEY"), getProvider());
}
步骤 4:创建测试
在您的测试目录下,创建这些文件。请注意,这些测试并不全面。它们测试由 OpenZeppelin 库提供的 ERC721 功能的一小部分,旨在为您提供创建更健壮测试的构建块。
test/MyNFT.spec.ts(单元测试)
任务规格(集成规格)
-- CODE language-js line-numbers --
import { deployTestContract, getTestWallet } from "./test-helper";
import { waffle, run } from "hardhat";
import { expect } from "chai";
import sinon from "sinon";
import * as provider from "../lib/provider";
describe("tasks", () => {
beforeEach(async () => {
sinon.stub(provider, "getProvider").returns(waffle.provider);
const wallet = getTestWallet();
sinon.stub(process, "env").value({
ETH_PUBLIC_KEY: wallet.address,
ETH_PRIVATE_KEY: wallet.privateKey,
});
});
describe("deploy-contract", () => {
it("calls through and returns the transaction object", async () => {
sinon.stub(process.stdout, "write");
await run("deploy-contract");
await expect(process.stdout.write).to.have.been.calledWith(
"Contract address: 0x610178dA211FEF7D417bC0e6FeD39F05609AD788"
);
});
});
describe("mint-nft", () => {
beforeEach(async () => {
const deployedContract = await deployTestContract("MyNFT");
process.env.NFT_CONTRACT_ADDRESS = deployedContract.address;
});
it("calls through and returns the transaction object", async () => {
sinon.stub(process.stdout, "write");
await run("mint-nft", { tokenUri: "https://example.com/record/4" });
await expect(process.stdout.write).to.have.been.calledWith(
"TX hash: 0xd1e60d34f92b18796080a7fcbcd8c2b2c009687daec12f8bb325ded6a81f5eed"
);
});
});
});
注意这需要导入 NPM 库,包括 sinon、chai 和 sinon-chai。由于使用了存根,所以 sinon.restore() 调用是必要的。
第五步:配置
这是我们的基本框架。
-- CODE language-js line-numbers --
import("@nomiclabs/hardhat-ethers");
import("@nomiclabs/hardhat-waffle");
import dotenv from "dotenv";
// You need to export an object to set up your config
// Go to https://hardhat.org/config/ to learn more
const argv = JSON.parse(env("npm_config_argv"));
if (argv.original !== ["hardhat", "test"]) {
require('dotenv').config();
}
import("./tasks/nft");
import { HardhatUserConfig } from "hardhat/config";
const config: HardhatUserConfig = {
solidity: "0.8.6",
};
export default config;
请注意,如果我们不运行测试,只调用 dotenv 的条件。您可能不希望在生产中运行它,但是请放心,如果。env 文件不存在。
运行我们的任务
现在我们已经把这些文件放好了,我们可以运行 hardhat 来查看我们的任务(为了简洁起见,不包括内置任务)。
-- CODE language-js line-numbers --
AVAILABLE TASKS:
deploy-contract Deploy NFT contract
mint-nft Mint an NFT
忘记你的任务的争论?没问题。
-- CODE language-js line-numbers --
$ hardhat help deploy-contract
Usage: hardhat [GLOBAL OPTIONS] deploy-contract
deploy-contract: Deploy NFT contract
运行我们的测试
为了运行我们的测试,我们运行安全帽测试。
-- CODE language-js line-numbers --
mintNft
✓ calls through and returns the transaction object (60ms)
MyNFT
mintNft
✓ emits the Transfer event (60ms)
✓ returns the new item ID
✓ increments the item ID (57ms)
✓ cannot mint to address zero
balanceOf
✓ gets the count of NFTs for this address
6 passing (2s)
✨ Done in 5.66s.
摘要
在本教程中,我们已经为经过充分测试的基于可靠性的 NFT 基础设施打下了坚实的基础。由waffle . provider . get wallets()提供的钱包链接到当地一个假冒的 Hardhat Network 账户,方便地预装了一个 eth 余额,我们可以用它来为我们的测试交易提供资金。