本文介绍了如何使用区块链技术革新葡萄酒行业,通过NFT确保葡萄酒的真实性、来源和安全所有权。文章详细描述了系统架构、智能合约设计和集成,这些都为基于区块链的葡萄酒交易市场提供了动力。重点介绍了WineCollection智能合约,它是在Arbitrum区块链上管理葡萄酒所有权、元数据和交易的ERC-721代币。
前一篇文章探讨了区块链技术如何通过 NFT 确保真实性、来源和安全所有权,从而彻底改变葡萄酒行业。我们详细介绍了我们在 Arbitrum 上构建葡萄酒厂解决方案的历程,其中每瓶葡萄酒都表示为具有透明交易历史记录的 NFT。该系统集成了区块链、IPFS 和网络市场,为酿酒商和买家提供无缝体验。
阅读完整文章:
https://learnblockchain.cn/article/14730
在本文中,我们将重点转移到技术实现,分解系统架构、智能合约设计以及为这个基于区块链的市场提供支持的集成。
前端 (HTML/JavaScript)
后端 (Java)
数据库 (MongoDB)
区块链 (Arbitrum L2 & ERC-721 智能合约)
存储 (通过 Pinata 的 IPFS)
外部服务
WineCollection 智能合约是一个 ERC-721 token,它将葡萄酒瓶表示为 NFT。 它利用 OpenZeppelin 库来实现安全性和最佳实践。
开发过程首先设计智能合约,该合约作为表示单次葡萄酒发行的基础。我们专注于定义核心功能,例如为每瓶酒铸造 NFT、跟踪所有权以及通过 IPFS 链接元数据。一旦合约巩固,我们就开始将其与前端和后端集成,确保区块链和网络市场之间的无缝交互。
ERC721
和 ERC721URIStorage
:提供标准的 NFT 功能,包括元数据存储。Ownable
:确保只有合约所有者才能执行管理操作。_supply
:跟踪已铸造的Token数量。_maxSupply
:定义此集合允许的 NFT 总数,即此葡萄酒发行中的最大瓶数。_contractURI
:存储有关整个集合的元数据。_reviewURI
:保存集合的评论摘要。_reviewURISet
:确保评论 URI 仅设置一次。我们使用智能合约中的 事件 来显式地创建一个 链上记录,每当相关值发生更改时。
TokenURIUpdated
:当 NFT 的元数据更改时发出。ContractURIUpdated
:当集合元数据更新时发出。ReviewURISet
:当发布专家评论时发出。使用以下内容初始化合约:
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract WineCollection is ERC721, ERC721URIStorage, Ownable {
uint32 private _supply;
uint32 private _maxSupply;
string private _contractURI;
string private _reviewURI;
bool private _reviewURISet;
event TokenURIUpdated(uint256 indexed tokenId, string newURI);
event ContractURIUpdated(string newContractURI);
event ReviewURISet(string reviewURI);
constructor(string memory contractName,
string memory contractSymbol,
address initialOwner,
uint32 maxSupply,
string memory __contractURI)
ERC721(contractName, contractSymbol)
Ownable(initialOwner)
{
require(maxSupply > 0, string(abi.encodePacked(contractName, ": maxSupply should be more than 0")));
_maxSupply = maxSupply;
_contractURI = __contractURI;
}
此代码处理设置和检索 contractURI
,它链接到描述葡萄酒系列合约的 IPFS 文件。
function contractURI() public view returns (string memory) {
return _contractURI;
}
function updateContractURI(string memory newContractURI) public onlyOwner {
_contractURI = newContractURI;
emit ContractURIUpdated(newContractURI);
}
此代码处理设置 tokenURI
,它链接到描述瓶子 NFT 的 IPFS 文件。
function updateTokenURI(uint256 tokenId, string memory newURI) public {
require(ownerOf(tokenId) == msg.sender, "updateTokenURI: Caller is not the owner ");
_setTokenURI(tokenId, newURI);
emit TokenURIUpdated(tokenId, newURI);
emit MetadataUpdate(tokenId);
}
safeMint()
函数允许合约所有者铸造新的 NFT,同时强制执行供应限制:
_supply
)不超过允许的最大数量(_maxSupply
)。_safeMint()
以安全地创建 NFT 并将其分配给指定的地址(to
)。_setTokenURI()
设置Token的元数据 URI,将其链接到 IPFS 文件。_supply
以跟踪铸造的 NFT 数量。function safeMint(address to, uint256 tokenId, string memory uri) public onlyOwner {
require(_supply < _maxSupply, "Max number of NFTs has been reached");
_safeMint(to, tokenId);
_setTokenURI(tokenId, uri);
_supply++;
}
safeMintBatch()
函数允许合约所有者在单个交易中铸造多个 NFT:
safeMint()
。虽然已经实施了这种批量铸造机制以实现未来的可扩展性,但我们目前只在列表中发送一个 token,从而有效地一次铸造一个 NFT。
function safeMintBatch(address to, uint256[] memory tokenIds, string[] memory uris) public onlyOwner {
require(tokenIds.length == uris.length, "Token IDs and URIs length mismatch");
for (uint256 i = 0; i < tokenIds.length; i++) {
safeMint(to, tokenIds[i], uris[i]);
}
}
此代码启用 NFT 所有权的转移,同时更新其元数据。
changeOwnerAndTokenURI
:更新 Token 的元数据(tokenURI
)并在单个交易中转移所有权。changeOwner
:确保只有当前所有者才能将 NFT 转移到新地址。function changeOwnerAndTokenURI(address to, uint256 tokenId, string memory newURI) public {
updateTokenURI(tokenId, newURI);
changeOwner(to, tokenId);
}
function changeOwner(address to, uint256 tokenId) private {
require(ownerOf(tokenId) == msg.sender, "changeOwner: Caller is not the owner");
_transfer(msg.sender, to, tokenId);
}
totalSupply
函数返回可以在集合中铸造的 NFT 的最大数量。 它允许用户在 arbiscan 界面中检查总可用供应量。
function totalSupply() public view returns (uint256) {
return _maxSupply;
}
burn()
函数允许合约所有者通过调用 _burn(tokenId)
永久删除 NFT,从而确保 token 被销毁并且不能再被转移或访问。
function burn(uint256 tokenId) public onlyOwner {
_burn(tokenId);
}
这些函数管理 review URI,它是一个 IPFS 文件,包含葡萄酒收藏的专家评论:
setReviewURI
:允许合约所有者设置 review URI,但只能设置一次。 这可确保专家评论在发布后保持不变。 会发出一个事件(ReviewURISet
)以在区块链上记录此更改。getReviewURI
:提供了一种检索存储的 review URI 的方法。function setReviewURI(string memory newReviewURI) public onlyOwner {
require(!_reviewURISet, "Review URI has already been set");
_reviewURI = newReviewURI;
_reviewURISet = true;
emit ReviewURISet(newReviewURI);
}
function getReviewURI() public view returns (string memory) {
return _reviewURI;
}
对于智能合约开发,我们使用了 Remix Online IDE https://remix.ethereum.org/,,这是一个基于 Web 的环境,专为编写、测试和部署 Solidity 合约而设计。
要将 Remix 与本地文件夹连接,你可以安装并在你的工作目录中运行 remixd:
yarn global add @remix-project/remixd
remixd
借助 Remix,我们能够在部署到 测试(Sepolia Arbitrum) 和 生产(Arbitrum)区块链之前,使用虚拟钱包在 Remix Virtual Machine 上运行测试。
要通过 ethers.js 与智能合约交互,我们需要提取 ABI (Application Binary Interface),它定义了合约的方法和结构。
Hardhat 是一个以太坊智能合约的开发环境,能够进行编译、部署、测试和调试。 我们使用 Hardhat 编译合约并使用以下命令提取 ABI 和字节码。
· 安装 Hardhat:
yarn add --dev hardhat
· 使用 CLI 向导创建一个 Hardhat 项目:
yarn hardhat init
这将提示在当前目录中创建一个新的 Hardhat 项目。 选择 “Create TypeScript project。
· 配置 hardhat.config.ts
import { HardhatUserConfig } from "hardhat/config";
const config: HardhatUserConfig = {
solidity: {
version: "0.8.20",
settings: {
optimizer: {
enabled: true, // Matches deployment settings
runs: 200, // Optimization runs (commonly set to 200)
},
},
},
sourcify: {
enabled: true, // Enables automatic Sourcify verification
},
};
export default config;
要使用 javascript 库 ethers.js 与智能合约交互,我们需要 ABI (Application Binary Interface)。
· 准备脚本 scripts/extractContractAbiByteCode.ts
:
import fs from 'fs';
import path from 'path';
const contractJsonPath = path.resolve(__dirname, '../artifacts/contracts/WineCollection.sol/WineCollection.json');
const contractJson = JSON.parse(fs.readFileSync(contractJsonPath, 'utf8'));
const { abi, bytecode } = contractJson;
const outputPath = path.resolve(__dirname, '../output2/WineCollectionAbiByteCode.json');
fs.writeFileSync(outputPath, JSON.stringify({ abi, bytecode }, null, 2));
console.log('ABI and bytecode extracted to ../output2/WineCollectionAbiByteCode.json');
· 运行 scripts/extractContractAbiByteCode.ts
并接收 ABI json 作为输出:
yarn hardhat run scripts/extractContractAbiByteCode.ts
Compiled 1 Solidity file successfully (evm target: paris).
ABI and bytecode extracted to ../output/WineCollectionAbiByteCode.json
要通过 REST API 验证合约,需要一个展平的 Solidity 文件。 我们使用以下命令生成它:
yarn hardhat flatten contracts/WineCollection.sol > ../output/WineCollection.flattened.sol
但是,hardhat 展平并不完美,我们需要手动清理输出文件:
pragma solidity ^0.8.20;
并删除任何重复项。这些步骤确保合约的格式正确,以便成功验证。
在 “构建基于区块链的葡萄酒市场:技术之旅” 的第一部分中,我们介绍了系统的 架构 和为基于 NFT 的葡萄酒真实性提供支持的 智能合约开发。 我们探讨了 WineCollection 合约如何管理 Arbitrum 区块链上的所有权、元数据和交易,从而确保透明度和安全性。
在下一部分中,我们将深入研究 前端 (JavaScript) 和后端 (Java) 实现,详细介绍 Web3 市场如何与区块链交互、处理元数据存储以及提供无缝的用户体验。 敬请关注!🚀
- 原文链接: coinsbench.com/building-...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!