初遇NFT-IPFS
初遇NFT-IPFS
本次学习如何使用Hardhat框架制作可预售的NFT并利用IPFS存储元数据。
-
NFT简介
-
NFT全称Non-fungible Token(即非同质化通证)。不可分割性(目前有碎片化协议暂且不谈)且独一无二。这有点对应现实资产比如古董、艺术品。即使是同一批厂家或者艺术家创作也难保证一模一样。
- NFT 目前主要用于艺术品、域名、虚拟资产、游戏。代表性的就是CryptoKitties、CryptoPunk、NBA Top Shot等
- NFT的价值就是仁者见仁智者见智了,稀有程度,品牌价值,认可度等都是维持价格的因素。------情怀、艺术无价。
- 常用的NFT工具:
- 交易网站Opensea:https://opensea.io/
- 市场数据分析:欧科云链 https://www.oklink.com/zh-cn/nft NFT流行应用https://degendata.io/
- NFT应用数据:Dappradder https://degendata.io/,给使用过的用户发过空投价值几百刀
- 重点推荐NFTSCAN https://nftscan.com/ 可以看到链上NFT的交易情况,也算挖掘最新NFT的常用工具
- 交易网站Opensea:https://opensea.io/
-
-
开发工具
-
hardhat :在以太坊上构建的开发环境。它帮助开发人员管理和自动化构建智能合约和 dApp 。需要先安装Node.js。Node.js安装教程 https://blog.csdn.net/fangchunyang/article/details/118178742
-
ipfs:InterPlanetary File System 星际文件系统。IPFS是一种内容可寻址、版本化、点对点超媒体的分布式存储、传输协议。在IPFS的网络里,是根据内容寻址,每一个上传到IPFS上面去的文件、文件夹,都是以Qm为开头字母的哈希值,无需知道文件存储在哪里,通过哈希值就能够找到这个文件,这种方式叫内容寻址。ipfs教程可以看 tiny熊大佬的附上传送门link。还需要使用Pinata 有免费版,这个可以上传文件。附上链接https://app.pinata.cloud/ 如果想在chrome访问ipfs 需要安装插件。附上链接 下载
-
Remix :Remix是以太坊智能合约编程语言Solidity的一个基于浏览器的IDE。附上链接https://remix.ethereum.org/
-
-
教程开始
- 默认已经安装配置好npm了如果没有请百度后按照。安装hardhat 并初始项目
-
1 mkdir nft_1 2 cd nft_1 3 npm init -y 4 npm install --save-dev hardhat
运行hardhat
npx hardhat
我们选择:basic sample project 然后全程默认回车
-
创建好的项目目录:
文件contracts 是存放我们写的智能合约
node_modules 是安装的一些插件
scripts 是部署合约调用合约
test 是编写测试脚本 - 现在来为hardhat 安装一些插件。
安装waffle 、ethers chai web3等插件
npm install --save-dev @nomiclabs/hardhat-ethers ethers @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-web3 web3 安装openzeppelin 合约 npm install @openzeppelin/contracts 安装验证etherscan插件 npm install --save-dev @nomiclabs/hardhat-etherscan 为了安全 使用.env npm install dotenv -
插件安装完成后我们需要在项目里添加。
- 找到文件 hardhat.config.js 编辑 然后添加我们刚才安装的插件
-
require("@nomiclabs/hardhat-waffle"); require("@nomiclabs/hardhat-web3"); require("@nomiclabs/hardhat-ethers"); require("@nomiclabs/hardhat-etherscan"); require('dotenv').config();
结果如图所示
- 现在来清理一些不要的文件,
- 删除contracts文件下的Greeter.sol
- 删除test文件下的sample-test.js
-
-
- 文件配置已经完成 接下来我们来编写智能合约
- 在contracts 中新建一个合约 MyNFT.sol
- 代码
//编译器版本 pragma solidity ^0.8.0; //导入openzeppelin库 //Ownable 是openzeppelin实现管理员 import '@openzeppelin/contracts/access/Ownable.sol'; //ERC721是NFT常用的标准协议 import '@openzeppelin/contracts/token/ERC721/ERC721.sol'; //可以枚举 import '@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol'; //我们创建的合约需要继承上面的合约 contract MyNFT is ERC721, ERC721Enumerable, Ownable { //开启售卖nft bool public _isSaleActive = false; // Constants //设置NFT数量 uint256 constant public MAX_SUPPLY = 2048; //售卖NFT的价格 uint256 public mintPrice = 0.01 ether; //账户最多拥有的NFT数量 uint256 public maxBalance = 6; //账户一次性最多铸造的数量 uint256 public maxMint = 3; //基本url string private _baseURIExtended; event TokenMinted(uint256 supply); event SaleStarted(); event SalePaused(); event AuctionStarted(); event AuctionPaused(); constructor() ERC721('MoonStart', 'ms') {} //开始预售 function startSale() public onlyOwner { _isSaleActive = true; emit SaleStarted(); } //暂停预售 function pauseSale() public onlyOwner { _isSaleActive = false; emit SalePaused(); } //设置价格 function setMintPrice(uint256 _mintPrice) public onlyOwner { mintPrice = _mintPrice; } //设置拥有NFT数量 function setMaxBalance(uint256 _maxBalance) public onlyOwner { maxBalance = _maxBalance; } //设置最大mint数量 function setMaxMint(uint256 _maxMint) public onlyOwner { maxMint = _maxMint; } //提取合约中的ETH function withdraw(address to) public onlyOwner { uint256 balance = address(this).balance; payable(to).transfer(balance); } //返回当前总量 function getTotalSupply() public view returns (uint256) { return totalSupply(); } //获取地址拥有的nft function getMoonStarByOwner(address _owner) public view returns (uint256[] memory) { uint256 tokenCount = balanceOf(_owner); uint256[] memory tokenIds = new uint256[](tokenCount); for (uint256 i; i < tokenCount; i++) { tokenIds[i] = tokenOfOwnerByIndex(_owner, i); } return tokenIds; } //mint NFT function mintMoonStar(uint numMoonStars) public payable { require(_isSaleActive, 'Sale must be active to mint MoonStars'); require(totalSupply() + numMoonStars <= MAX_SUPPLY, 'Sale would exceed max supply'); require(balanceOf(msg.sender) + numMoonStars <= maxBalance, 'Sale would exceed max balance'); require(numMoonStars <= maxMint, 'Sale would exceed max mint'); require(numMoonStars * mintPrice <= msg.value, 'Not enough ether sent'); _mintMoonStar(numMoonStars, msg.sender); emit TokenMinted(totalSupply()); } //mint 函数具体实现 function _mintMoonStar(uint256 numMoonStars, address recipient) internal { uint256 supply = totalSupply(); for (uint256 i = 0; i < numMoonStars; i++) { _safeMint(recipient, supply + i); } } //设置基本url 主要后面拼接 function setBaseURI(string memory baseURI_) external onlyOwner { _baseURIExtended = baseURI_; } //获取基本url function _baseURI() internal view virtual override returns (string memory) { return _baseURIExtended; } //得到nft的url function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { require(_exists(tokenId), 'ERC721Metadata: URI query for nonexistent token'); return string(abi.encodePacked(_baseURI(), tokenId.toString())); } //转移这里使用了hooks function _beforeTokenTransfer( address from, address to, uint256 tokenId ) internal override(ERC721, ERC721Enumerable) { super._beforeTokenTransfer(from, to, tokenId); } // IERC165 接口的实现 function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC721Enumerable) returns (bool) { return super.supportsInterface(interfaceId); } }
- 代码
- 在scripts文件创建run.js
- 代码
const main = async () => { const nftContractFactory = await hre.ethers.getContractFactory('MyNFT'); const nftContract = await nftContractFactory.deploy(); await nftContract.deployed(); console.log("Contract deployed to:", nftContract.address); }; const runMain = async () => { try { await main(); process.exit(0); } catch (error) { console.log(error); process.exit(1); } }; runMain();
由于我喜欢在remix 调试,这里就只用部署合约,没有进行函数调用
- 我们直接使用hardhat的测试网先试着编译运行一次。
npx hardhat run scripts/run.js
无报错,智能合约没问题。
- 代码
- 在contracts 中新建一个合约 MyNFT.sol
- 现在我们来部署在Rinkeby链上,因为它有opensea来显示我们的NFT。如果测试网没有eth,可以去水龙头领,我常用的是chainlink的 https://faucets.chain.link/rinkeby 很好用,就是每次量少了一些,多点几次。
- 修改hardhat.config.js
- 初始的module.exports
module.exports = { solidity: "0.8.4", };
修改完成的是
module.exports = { solidity: "0.8.4", networks: { rinkeby: { url:process.env.STAGING_ALCHEMY_KEY, accounts:[process.env.PRIVATE_KEY], }, },
etherscan: {
apiKey:process.env.API_KEY
}
};- url:我们使用节点才能和智能合约交互,市面上有很多节点 常用的infura https://infura.io/, alchemy https://www.alchemy.com/ 我这里使用的alchemy,只需要注册账户、新建项目然后复制网站给我们的key 格式差不多是 https://eth-rinkeby.alchemyapi.io/v2/xxxxxxxxxxxxx
- accounts 这里需要提供部账户的私钥.为了安全我们使用了env
- apiKey: 这是为了将我们的智能合约通过etherscan的验证 ,需要注册一个账户 并且复制apikey https://etherscan.io
- 初始的module.exports
- 创建.env
- 内容 url STAGING_ALCHEMY_KEY="你的key https://eth-rinkeby.alchemyapi.io/v2/xxxxxxxxxxxxx"
- 私钥 PRIVATE_KEY='xxxxxxx'
- API_KEY='xxxxx'
- hardhat 利用 hardhat-etherscan 插件自动在etherscan 验证合约失败,大概是网络问题但我科 学也失败了,有解决办法的老哥可以交流下。因此我们需要手动去etherscan 验证合约。后面写一篇如何怎么在etherscan 验证合约
- 首先我们在rineby 部署合约,
-
npx hardhat run scripts/run.js --network rinkeby
上面的0xe4 就是合约地址,部署成功,我们现在打开remix https://remix.ethereum.org/
- 在remix中新建一个合约,粘贴智能合约代码 MyNFT.sol
-
点击左边栏的 Deploy & run transactions 在ENVIRIONMENT 选择Injected Web3 这个可以让remix 链接我们的小狐狸钱包,然后再CONTRACT 找到 MyNFT -contracts/MyNFT.sol接着下面 At Address 粘贴我们上面得到的合约地址,然后点击 At Address。
-
现在我们来交互我们的智能合约, 点击箭头部分,再找到startsale 必须使用部署这个合约的账号
-
先开启 售卖
-
我们再来mint 1个NFT,此时需要我们发送eth。由于在remix 无法直接填写0.01ether,需要单位换成'wei' 这是最小单位10000000000000000 wei==0.01ether 1ether==10^18 wei。我这里演示mint 2个
看链上返回结果,已经成功。
-
-
- 现在是NFT已经铸造好了,但是没有NFT的一些信息。NFT的信息我们成为元数据 他是一个json 格式 我们通过函数 tokenURI 让我们的NFT加上元数据
{ "name": "MoonStart", "description": "test NFT", "attributes": [ { "trait_type": "Background", "value": "Galaxy" }, { "trait_type": "color", "value": "blue" } ], "image": "https://ipfs.io/ipfs/xxx/1.png" }
- 我们先新建一个文件夹存放我们的图片,图片名字 1.jpg ....然后上传到ipfs.ipfs我们可以使用pinata 网站 https://app.pinata.cloud/
就像这样,我们需要复制CID,待会添加到json里
- 现在来新建文件 来存放元数据 image 哪里填写我们刚刚上传的图片的 CID。
然后又通过pinata上传
然后复制CID,返回remix中 调用函数 setbaseURI
这里我们填写的是 ipfs://Qmxxxx/
- 然后我们耐心等待一段时间,打开opensea的测试网: https://testnets.opensea.io/
未完待续---------散花
- 我是等了很久,如果pinata 太慢了 建议大家下个ipfs桌面版。
- 修改hardhat.config.js
- 默认已经安装配置好npm了如果没有请百度后按照。安装hardhat 并初始项目
在验证合约这里折腾了很久,但也学习到了很多。对应这种导入了第三方库的合约 解决办法就是通过 flattener。remix-ide本地版本有这个插件 flattener ,或者使用truffle-flattener把这些合约全都压缩在一个文件 并删除多余的spdx。最佳的方式还是使用插件来自动化验证比如 hardhat truffle 的自动验证合约。
新年快乐
本文来自博客园,作者:soth912,联系方式QQ:1161510735 互相学习转载请注明原文链接:https://www.cnblogs.com/soth912/p/15741279.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通