NFT 合约部署教程

本篇文章主要介绍如何将您的 NFT(ERC-721 Token) 通过智能合约部署到去中心化网络中

Init Project

 
//创建一款ocean的NFT
mkdir  nft-ocean//进入目录
cd nft-ocean//初始化项目,根据提示填写即可,packname和description填写即可
npm init//添加hardhat依赖
npm install --save-dev hardhat/*使用脚手架搭建项目,我们选择Create a basic sample project
可以帮助我们创建1个demo工程并按照所需的依赖*/
npx hardhat

这是初始化完成后的代码结构

  • contracts目录用来存放我们的智能合约代码

  • scripts用来存放我们的脚本,比如合约部署就会依赖其中的脚本

  • test目录用来存放我们为智能合约编写的测试代码

  • hardhat.config.js是关于hardhat框架的一些配置,比如solidity版本等

  • package.json是npm的相关配置

编写合约代码

目前就可以开始写合约代码了,本文主要是介绍NFT的发行,我们简单来实现一份NFT智能合约代码。

//安装openzeppelin/contracts依赖,内置较多合约协议的实现以及工具代码
npm install @openzeppelin/contracts

我们在contracts目录下创建NFT_OCEAN.sol的文件。

pragma solidity ^0.8.0;import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";contract NFT_OCEAN is ERC721, ERC721Enumerable, Ownable {string private _baseURIextended;//Set mint limituint256 public constant MAX_SUPPLY = 5;//0.01eth per mintuint256 public constant PRICE_PER_TOKEN = 0.01 ether;constructor() ERC721("nft_ocean", "NFT_OCEAN") {}function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal override(ERC721, ERC721Enumerable) {super._beforeTokenTransfer(from, to, firstTokenId, batchSize);}function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, ERC721Enumerable) returns (bool) {return super.supportsInterface(interfaceId);}function setBaseURI(string memory baseURI_) external onlyOwner() {_baseURIextended = baseURI_;}function _baseURI() internal view virtual override returns (string memory) {return _baseURIextended;}function mint(uint numberOfTokens) public payable {uint256 ts = totalSupply();require(ts + numberOfTokens <= MAX_SUPPLY, "Purchase would exceed max tokens");require(PRICE_PER_TOKEN * numberOfTokens <= msg.value, "Ether value sent is not correct");for (uint256 i = 0; i < numberOfTokens; i++) {_safeMint(msg.sender, ts + i);}}function withdraw() public onlyOwner {uint balance = address(this).balance;payable(msg.sender).transfer(balance);}
}

该代码不具备上线标准,因为没有白名单。每个地址限制mint多少个等,仅供示意。建议发行NFT前需要好好设计合约

合约编译

//合约编译
npx hardhat compile

合约部署

修改scripts目录下自动生成的脚本改名为deploy.js并修改内部代码

const hre = require("hardhat");async function main() {// Hardhat always runs the compile task when running scripts with its command// line interface.//// If this script is run directly using `node` you may want to call compile// manually to make sure everything is compiled// await hre.run('compile');// We get the contract to deployconst Nft_ocean = await hre.ethers.getContractFactory("NFT_OCEAN");const nft_ocean = await Nft_ocean.deploy();await nft_ocean.deployed();console.log("NFT_OCEAN deployed to:", nft_ocean.address);
}// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().then(() => process.exit(0)).catch((error) => {console.error(error);process.exit(1);}); 

选择节点服务代理

有很多方法可以向以太坊区块链发出请求,但为了让事情变得简单,我们将使用 Alchemy 上的免费帐户,这是一个区块链开发人员平台和 API,允许我们与以太坊链,无需运行我们自己的节点。创建过程参考文档,分别创建一个Ethereum Mainnet 和 Ethereum Sepolia 的APP

测试网络环境搭建

我们首先需要在测试网络中部署,我们选择Sepolia作为我们的测试网络。

因为在代码执行过程中会用到公钥、私钥、API等数据,但这些数据又不能编码到代码中,因为如果我们使用git来管理项目,信息将会泄露。我们使用dotenv存放部署合约以及和合约交互需要用到的数据。

//添加dotenv依赖
npm install dotenv//工程根目录下创建变量存储文件
touch .env

为.env增加内容

PRIVATE_KEY= 导出你metamask的私钥填在这里
API=Alchemy APP中的HTTPS
PUBLIC_KEY= metamask地址
NETWORK=sepolia
API_KEY=Alchemy的API KEY
  • metamask私钥导出:打开钱包-账号详情->导出私钥。

  • API、API_KEY:打开Alchemy 中 Ethereum Sepolia 的API KEY,复制HTTPS和API_KEY

修改hardhat.config.js


/**
* @type import('hardhat/config').HardhatUserConfig
*/require("@nomicfoundation/hardhat-toolbox");
require("@nomiclabs/hardhat-ethers");
//require("@nomiclabs/hardhat-waffle");// This is a sample Hardhat task. To learn how to create your own go to
// https://hardhat.org/guides/create-task.html
require('dotenv').config();
const { API, PRIVATE_KEY } = process.env;// You need to export an object to set up your config
// Go to https://hardhat.org/config/ to learn more/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {solidity: "0.8.19",defaultNetwork: "sepolia",networks: {hardhat: {},sepolia: {url: API,accounts: [`0x${PRIVATE_KEY}`]}},
};

获取测试网络ETH 因为合约测试需要消耗ETH,我们需要获取一些测试用的ETH,我们打开https://sepoliafaucet.com/,填入我们的钱包地址即可获取。

此时我们将钱包切换到sepolia测试网络,可以看到钱包中已经有一些ETH了。

执行合约部署

首先安装 ETHERS.JS

npm install --save-dev @nomiclabs/hardhat-ethers ethers@^5.7.2

执行合约部署

npx hardhat --network sepolia run scripts/deploy.js
//执行完成后得到提示,代表合约部署完成
NFT_WEB3_EXPOLRER deployed to: {合约地址}

此时我们可以在https://sepolia.etherscan.io/搜索我们的合约地址找到我们的合约信息

为NFT设置资源

现在的NFT仅仅包含tokenID,我们需要为其设置资源, 在ERC721的实现中我们可以看到tokenURI方法的实现,各个交易市场就是调用该方法来读取NFT资源信息的

/*** @dev See {IERC721Metadata-tokenURI}.*/function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");string memory baseURI = _baseURI();return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";}

我们可以看到这里是将BaseURI和tokenID拼接来读取tokenURI的,所以我们也按照这种格式来准备资源。

准备资源

//创建资源文件
mkdir res
cd res
//存放图片
mkdir  nfts
//存放metadata信息
mkdir  metadata

由于本文是示例项目,没有找专门的设计师设计图片,我们网上找了5张图片。将5张图片放入img目录下,依次命名为1.jpeg ~ 5.jpeg

我们资源文件上传到ipfs中,这里和以太坊网络一样,由于我们不想运行本地ipfs节点,所以我们寻找节点服务提供商进行上传。我们使用https://dashboard.4everland.org/bucket/storage/将nfts文件夹上传

上传完成后的图片资源

选中图片文件夹进行snapshots,即可生成root cid,网关访问效果如下:

接下来我们准备metadata文件,在metadata目录下新建5个文件依次命名为0~4,我们看下0号文件信息

{"name": "nft-ocean","attributes": [{"trait_type": "tokenID","value": "0"}],"description": "nft-ocean image",//填入上面刚刚上传完成的图片地址"image": "ipfs://bafybeief75v57gu3khb5ftjkgldyzyea4x3r6sesekcia6lk2pijgl5idm/01.jpeg"
}

我们将metadata文件夹上传并snapshot,获取文件夹CID后进行publish

设置BaseURI

我们需要将刚刚上传后的metadata文件地址设置给合约的baseURI,这样各个平台在使用tokenURI获取资源信息才可以获取到。

编写代码与合约交互设置BaseURI,在scripts目录下新建setBaseURI.js文件。

setBaseURI.js

require("dotenv").config()
const hre = require("hardhat");
const PRIVATE_KEY = process.env.PRIVATE_KEY
const NETWORK = process.env.NETWORK
const API_KEY = process.env.API_KEYconst provider = new hre.ethers.providers.AlchemyProvider("sepolia",API_KEY);
//编译完成合约会自动生成
const abi = require("../artifacts/contracts/NFT_OCEAN.sol/NFT_OCEAN.json").abi
const contractAddress = "填合约地址" 
const contract = new hre.ethers.Contract(contractAddress, abi, provider)
const wallet = new hre.ethers.Wallet(PRIVATE_KEY, provider)
const baseURI = "ipfs://{metadata文件的root cid}/" //填async function main() {const contractWithSigner = contract.connect(wallet);//调用setBaseURI方法const tx = await contractWithSigner.setBaseURI(baseURI)console.log(tx.hash);await tx.wait();console.log("setBaseURL success");
}main().then(() => process.exit(0)).catch((error) => {console.error(error);process.exit(1);});

执行setBaseURI脚本

npx hardhat --network sepolia run scripts/setBaseURI.js

Mint 测试

因为只有mint过的tokenID才会展示在交易市场中,所以我们需要编写代码进行mint测试(实际场景,该操作应该由前端页面调用完成)。

mint.js

require("dotenv").config()
const hre = require("hardhat");
const PRIVATE_KEY = process.env.PRIVATE_KEY
const NETWORK = process.env.NETWORK
const API_KEY = process.env.API_KEYconst provider = new hre.ethers.providers.AlchemyProvider("sepolia",API_KEY);
const abi = require("../artifacts/contracts/NFT_OCEAN.sol/NFT_OCEAN.json").abi
const contractAddress = "0x531D6B8fBe5FAC349D05642E17F5E998A4DfEd68"
const contract = new hre.ethers.Contract(contractAddress, abi, provider)
const wallet = new hre.ethers.Wallet(PRIVATE_KEY, provider)async function main() {const contractWithSigner = contract.connect(wallet);//获取mint需要多少ETHconst price = await contract.PRICE_PER_TOKEN();console.log("price is" + price);//调用mint方法,支付mint费用const tx = await contractWithSigner.mint(1, { value: price});console.log(tx.hash);await tx.wait();console.log("mint success");
}main().then(() => process.exit(0)).catch((error) => {console.error(error);process.exit(1);});

执行mint脚本

npx hardhat --network sepolia run scripts/mint.js

Mint 成功

查看NFT

此时我们去opensea 测试网 https://testnets.opensea.io/ 查看我们mint的NFT 即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/73015.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

QT Creator更改主题和编辑器风格(附几款黑色主题)

适用于qtcreator 一、使用自带主题与编辑器风格 打开Qt选择"工具"->"选项"&#xff1b; 2. 选择"环境"->"Theme"切换不同的主题风格 这里切换的是外边框的风格&#xff0c;如果编辑器中有同名的风格&#xff0c;编辑器的风格也…

数据可视化:四大发明的现代转化引擎

在科技和工业的蓬勃发展中&#xff0c;中国的四大发明——造纸术、印刷术、火药和指南针&#xff0c;早已不再是古代创新的象征&#xff0c;而是催生了众多衍生行业的崭新可能性。其中&#xff0c;数据可视化技术正成为这些行业的一颗璀璨明珠&#xff0c;开启了全新的时代。 1…

(数字图像处理MATLAB+Python)第十二章图像编码-第三、四节:有损编码和JPEG

文章目录 一&#xff1a;有损编码&#xff08;1&#xff09;预测编码A&#xff1a;概述B&#xff1a;DM编码C&#xff1a;最优预测器 &#xff08;2&#xff09;变换编码A&#xff1a;概述B&#xff1a;实现变换编码的主要问题 二&#xff1a;JPEG 一&#xff1a;有损编码 &am…

基于ArcGIS、ENVI、InVEST、FRAGSTATS等多技术融合提升环境、生态、水文、土地、土壤、农业、大气等领域的数据分析能力与项目科研水平教程

详情点击链接&#xff1a;基于ArcGIS、ENVI、InVEST、FRAGSTATS等多技术融合提升环境、生态、水文、土地、土壤、农业、大气等领域的数据分析能力与项目科研水平教程 一&#xff0c;空间数据获取与制图 1.1 软件安装与应用 1.2 空间数据 1.3海量空间数据下载 1.4 ArcGIS软件…

MATLAB中M文件编写

简介 所谓M文件就是将处理问题的各种命令融合到一个文件中&#xff0c;该文件以.m为扩展名。然后&#xff0c;由MATLAB系统编译M文件&#xff0c;得出相应的运行结果。M文件具有相当大的可开发性和扩展性。M文件有脚本文件和函数文件两种。脚本文件不需要输入参数&#xff0c;…

C++QT day3

1> 自行封装一个栈的类&#xff0c;包含私有成员属性&#xff1a;栈的数组、记录栈顶的变量 成员函数完成&#xff1a;构造函数、析构函数、拷贝构造函数、入栈、出栈、清空栈、判空、判满、获取栈顶元素、求栈的大小 2> 自行封装一个循环顺序队列的类&#xff0c;包含…

计算机视觉领域经典模型汇总(2023.09.08

一、RCNN系列 1、RCNN RCNN是用于目标检测的经典方法&#xff0c;其核心思想是将目标检测任务分解为两个主要步骤&#xff1a;候选区域生成和目标分类。 候选区域生成&#xff1a;RCNN的第一步是生成可能包含目标的候选区域&#xff0c;RCNN使用传统的计算机视觉技术&#x…

【0908练习】shell脚本使用expr截取网址

题目&#xff1a; 终端输入网址&#xff0c;如&#xff1a;www.hqyj.com&#xff0c; 要求&#xff1a;截取网址每个部分&#xff0c;并放入数组中&#xff0c;不能使用cut&#xff0c;使用expr解决 #!/bin/bash read -p "请输入一个网址" net lenexpr length $net …

Unity中Shader的屏幕抓取 GrabPass

文章目录 前言一、抓取1、抓取指令2、在使用抓取的屏幕前&#xff0c;需要像使用属性一样定义一下,_GrabTexture这个名字是Unity定义好的 前言 Unity中Shader的屏幕抓取 GrabPass 一、抓取 1、抓取指令 屏幕的抓取需要使用一个Pass GrabPass{} GrabPass{“NAME”} 2、在使用…

TGA格式文件转材质

今天淘宝上买了一个美女的模型&#xff0c;是blender的源文件&#xff0c;上面说有fbx格式的。我用unity&#xff0c;所以觉得应该可以用。文件内容如下图&#xff1a; FBX文件夹打开后&#xff0c;内容如下图所示&#xff0c;当时就预感到可能没有色彩。 unity打开后果然发现只…

CSS的break-inside 属性 的使用

break-inside 属性在 CSS 页码分隔模块中使用,它定义了一个元素内部是否允许发生页面、栏目或者区域的分隔。 break-inside有以下几个值 break-inside: avoid- 表示避免在该元素内部发生分页或者分栏。break-inside: auto - 默认允许分页break-inside: avoid-page - 避免页面…

Java虚拟机反射机制

1 什么是Java虚拟机反射机制&#xff1f; 虚拟机在运行期间&#xff0c;对于任何一个类&#xff0c;我们都能知道其内部信息&#xff0c;包括属性&#xff0c;方法&#xff0c;构造函数&#xff0c;实现接口&#xff1b;对于任何一个对象&#xff0c;我们都能获取其字段值、调…

Git 客户端基本使用及新手常见问题

Git作为一个版本管理工具&#xff0c;在企业中的应用越来越普遍。作为一个测试工程师&#xff0c;不可避免会需要接触到Git的相关操作&#xff0c;以下整理Git客户端的常见操作&#xff0c;以及应用中新手常碰到的一些问题。 1、环境安装及配置 Git下载地址&#xff1a;https…

手敲Cocos简易地图编辑器:人生地图是一本不断修改的书,每一次编辑都是为了克服新的阻挡

引言 本系列是《8年主程手把手打造Cocos独立游戏开发框架》&#xff0c;欢迎大家关注分享收藏订阅。 在上一篇文章&#xff0c;笔者给大家讲解了在Cocos独立游戏开发框架中&#xff0c;如何自定义实现Tile地图管理器&#xff0c;成功地在游戏中优化加载一张特大的地图。接下来…

springboot~静态资源配置

springboot~静态资源配置 springboot的静态资源默认路径给静态资源请求加前缀指定静态资源加载的包欢迎页 springboot的静态资源默认路径 请求效果见图 给静态资源请求加前缀 有时需要用拦截器对某些请求进行拦截后再处理相关的业务代码&#xff0c;此时我们就要对请求进行…

腾讯云PK阿里云2核2G云服务器租用价格表

2核2G云服务器可以选择阿里云服务器或腾讯云服务器&#xff0c;腾讯云轻量2核2G3M带宽服务器95元一年&#xff0c;阿里云轻量2核2G3M带宽优惠价108元一年&#xff0c;不只是轻量应用服务器&#xff0c;阿里云还可以选择ECS云服务器u1&#xff0c;腾讯云也可以选择CVM标准型S5云…

ICIF2023化工展首亮相,宏工科技解决方案助力制造升级

ICIF China 2023中国国际化工展览会于9月4日-6日在上海新国际博览中心举办。宏工科技携化工物料处理一站式解决方案首次亮相&#xff0c;同化工行业全产业链共叙物料处理自动化未来。 宏工科技是一家提供物料处理自动化设备、系统与服务的国家级高新技术企业&#xff0c;业务覆…

uniapp 微信小程序最新隐私弹窗更新方案,更新后无法登录问题解决方案

1&#xff0c;在manifest.json文件中的mp-weixin 节点下&#xff0c;添加&#xff1a;"__usePrivacyCheck__": true 2&#xff0c;在需要的页面配置隐私保护弹窗&#xff0c;或者直接写到首页也可以 <uni-popup ref"popusAuthorization" type"cen…

overleaf 参考文献引用,创建引用目录.bib文件,在文档中引用参考文献,生成参考文献列表

目录 1 创建一个Overleaf项目 2 导入或创建 .bib 文件 2.1 导入 .bib 文件&#xff1a; 参考文献的 .bib文件获取步骤 &#xff08;1&#xff09;打开谷歌学术 &#xff08;2&#xff09;输入文献题目 &#xff08;3&#xff09;点击引用&#xff0c;然后选择BibTex格式…

opencv基础: 视频,摄像头读取与保存的常用方法

当然还可以从视频中抓取截图&#xff0c;所以现在聊一下常用的抓取视频截图的的方法。 VideoCapture 方法 cv2.VideoCapture();cv2.VideoCapture( device);cv2.VideoCapture(filename);上面有三种构造方法&#xff0c; 第一种是无法构造方法。 第二种参数device是一个数字。 …