{"content":{"title":"使用 Viem.sh 读取NFT合约信息","body":"# 使用 Viem.sh 读取NFT合约信息\r\n\r\n### 一、安装并配置viem\r\n\r\n要使用 `Viem.sh` 读取特定 NFT 合约中的信息，首先，你需要确保在项目中已经安装并配置了 `@viem/sh` 库。如果还没有安装，可以使用以下命令进行安装：\r\n\r\n```\r\npnpm install viem\r\n```\r\n\r\n![image-20240712213756873.png](https://img.learnblockchain.cn/attachments/2024/07/JgcTrDpV66913e82ef030.png)\r\n\r\n### 二、准备工作\r\n\r\n新建`viem_nft1`文件夹和文件`index.js`\r\n\r\n```\r\nmkdir viem_nft1\r\ncd viem_nft1 && vim index.js\r\n```\r\n\r\n在此，需要**连接到以太坊网络**：你需要使用 `Viem` 库连接到以太坊网络，例如 Infura 或 Alchemy 提供的节点。\r\n\r\n其次，**定义合约和调用函数**：你需要定义 NFT 合约的 ABI 和地址，并调用相应的函数来读取持有人地址和元数据。\r\n\r\n你可以替换`NFT_TOKEN_ID` 为你实际的 NFT 的 Token ID。\r\n\r\n编辑文件`index.js`内容如下：\r\n\r\n```\r\nimport { createPublicClient, http } from 'viem'\r\nimport { mainnet } from 'viem/chains';\r\nimport erc721Abi from './erc721Abi.json' assert { type: 'json' }; \r\n\r\nconst client = createPublicClient({\r\n  chain: mainnet,\r\n  transport: http(),\r\n})\r\n\r\nconst nftContractAddress = '0x0483b0dfc6c78062b9e999a82ffb795925381415'\r\nconst tokenId = 'NFT_TOKEN_ID' \r\n\r\n// 读取持有人地址\r\nasync function getOwnerOf(tokenId) {\r\n  const owner = await client.readContract({\r\n    address: nftContractAddress,\r\n    abi: erc721Abi,\r\n    functionName: 'ownerOf',\r\n    args: [tokenId],\r\n  })\r\n  return owner\r\n}\r\n\r\n// 读取元数据 URI\r\nasync function getTokenURI(tokenId) {\r\n  const tokenURI = await client.readContract({\r\n    address: nftContractAddress,\r\n    abi: erc721Abi,\r\n    functionName: 'tokenURI',\r\n    args: [tokenId],\r\n  })\r\n  return tokenURI\r\n}\r\n\r\n// 主函数\r\nasync function main() {\r\n  try {\r\n    const owner = await getOwnerOf(tokenId)\r\n    console.log(`NFT持有人地址: ${owner}`)\r\n\r\n    const tokenURI = await getTokenURI(tokenId)\r\n    console.log(`NFT元数据URI: ${tokenURI}`)\r\n\r\n    // 如果需要进一步获取元数据，可以发起HTTP请求获取元数据\r\n    const response = await fetch(tokenURI)\r\n    const metadata = await response.json()\r\n    console.log('NFT元数据:', metadata)\r\n  } catch (error) {\r\n    console.error('读取NFT信息时出错:', error)\r\n  }\r\n}\r\n\r\nmain()\r\n\r\n```\r\n\r\n为了读取 NFT 合约的信息，你需要有该合约的 ABI（应用二进制接口）文件。你可以创建一个 `erc721Abi.json` 文件，其中包含 ERC-721 标准合约的 ABI。\r\n\r\n打开浏览器，依次按照进行点击红框，往下拉寻找NFT合约的ABI\r\n\r\n\r\n![image-20240712215140324.png](https://img.learnblockchain.cn/attachments/2024/07/tKvbbURB66913e9cddbcd.png)\r\n\r\n点击按钮，复制文件\r\n\r\n![image-20240712215636474.png](https://img.learnblockchain.cn/attachments/2024/07/4ejUMtef66913ea6094e6.png)\r\n\r\n编辑文件 `erc721Abi.json` 内容复制粘贴如下：\r\n\r\n```\r\n[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"OwnableInvalidOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"OwnableUnauthorizedAccount\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"approved\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"ApprovalForAll\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"Price\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"TotalNum\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_address\",\"type\":\"address\"}],\"name\":\"addToBlacklist\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"numberOfTokens\",\"type\":\"uint256\"}],\"name\":\"airdrop\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"blacklist\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"getApproved\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"}],\"name\":\"isApprovedForAll\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"isContract\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"listClaimed\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"mint\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"nextOwnerToExplicitlySet\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"ownerOf\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"publicMintEnabled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_address\",\"type\":\"address\"}],\"name\":\"removeFromBlacklist\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_tokenId\",\"type\":\"uint256\"}],\"name\":\"safeTransferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_tokenId\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"}],\"name\":\"safeTransferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"setApprovalForAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"baseURI_\",\"type\":\"string\"}],\"name\":\"setBaseURI\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newPrice\",\"type\":\"uint256\"}],\"name\":\"setPrice\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"enable\",\"type\":\"bool\"}],\"name\":\"setPublicMint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"mroot\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"step\",\"type\":\"uint256\"}],\"name\":\"setRoot\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"enable\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"step\",\"type\":\"uint256\"}],\"name\":\"setWlMint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"tokenByIndex\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"tokenOfOwnerByIndex\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_tokenId\",\"type\":\"uint256\"}],\"name\":\"tokenURI\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_tokenId\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"wlMintStep1Enabled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"wlMintStep2Enabled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"proof\",\"type\":\"bytes32[]\"}],\"name\":\"wlmint1\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"proof\",\"type\":\"bytes32[]\"}],\"name\":\"wlmint2\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"wlroot1\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"wlroot2\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]\r\n```\r\n\r\n### 三、运行项目\r\n\r\n首先，在你的项目目录中运行以下命令来初始化一个新的项目：\r\n\r\n```\r\npnpm init\r\n```\r\n\r\n按照提示填写项目名称、版本等信息。完成后，会生成一个 `package.json` 文件。\r\n\r\n```\r\n{\r\n  \"name\": \"viem_nft1\",\r\n  \"version\": \"1.0.0\",\r\n  \"description\": \"\",\r\n  \"main\": \"index.js\",\r\n  \"scripts\": {\r\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\r\n  },\r\n  \"keywords\": [],\r\n  \"author\": \"\",\r\n  \"license\": \"ISC\"\r\n}\r\n```\r\n\r\n在脚本文件中添加构建模块和依赖\r\n\r\n```\r\n{\r\n  \"name\": \"viem_nft1\",\r\n  \"version\": \"1.0.0\",\r\n  \"description\": \"\",\r\n  \"main\": \"index.js\",\r\n  \"type\": \"module\",  \r\n  \"scripts\": {\r\n    \"start\": \"node index.js\",\r\n    \"build\": \"echo 'Build step, e.g., webpack or babel'\",\r\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\r\n  },\r\n  \"dependencies\": {\r\n    \"viem\": \"^2.17.3\" \r\n  },\r\n  \"keywords\": [],\r\n  \"author\": \"\",\r\n  \"license\": \"ISC\"\r\n}\r\n```\r\n\r\n运行以下命令安装依赖构建项目：\r\n\r\n```\r\npnpm install\r\npnpm build\r\n```\r\n\r\n填写'NFT_TOKEN_ID' ，比如为1，最后，运行以下命令启动项目：\r\n\r\n```\r\npnpm start\r\n```\r\n\r\n这里会得出运行结果但是会报一个错误，这个错误是因为 `fetch` 函数不能直接处理 `ipfs://` 协议。为了读取 IPFS 上的内容，你需要通过一个网关来访问 IPFS URL，比如 `https://ipfs.io/ipfs/` 或者其他公共 IPFS 网关。\r\n\r\n你可以将 IPFS URL 替换为通过公共网关访问的 HTTP URL。下面是更新后的代码：\r\n\r\n```\r\nimport { createPublicClient, http } from 'viem'\r\nimport { mainnet } from 'viem/chains';\r\nimport erc721Abi from './erc721Abi.json' assert { type: 'json' }; \r\n\r\nconst client = createPublicClient({\r\n  chain: mainnet,\r\n  transport: http(),\r\n})\r\n\r\nconst nftContractAddress = '0x0483b0dfc6c78062b9e999a82ffb795925381415'\r\nconst tokenId = '1' \r\n\r\n// 读取持有人地址\r\nasync function getOwnerOf(tokenId) {\r\n  const owner = await client.readContract({\r\n    address: nftContractAddress,\r\n    abi: erc721Abi,\r\n    functionName: 'ownerOf',\r\n    args: [tokenId],\r\n  })\r\n  return owner\r\n}\r\n\r\n// 读取元数据 URI\r\nasync function getTokenURI(tokenId) {\r\n  const tokenURI = await client.readContract({\r\n    address: nftContractAddress,\r\n    abi: erc721Abi,\r\n    functionName: 'tokenURI',\r\n    args: [tokenId],\r\n  })\r\n  return tokenURI\r\n}\r\n// 将 IPFS URL 转换为 HTTP URL\r\nfunction convertIpfsUrl(ipfsUrl) {\r\n  return ipfsUrl.replace('ipfs://', 'https://ipfs.io/ipfs/');\r\n}\r\n\r\n// 主函数\r\nasync function main() {\r\n  try {\r\n    const owner = await getOwnerOf(tokenId);\r\n    console.log(`NFT持有人地址: ${owner}`);\r\n\r\n    const tokenURI = await getTokenURI(tokenId);\r\n    console.log(`NFT元数据URI: ${tokenURI}`);\r\n\r\n    // 将 IPFS URL 转换为 HTTP URL\r\n    const httpUrl = convertIpfsUrl(tokenURI);\r\n    console.log(`转换后的HTTP URL: ${httpUrl}`);\r\n\r\n    // 发起HTTP请求获取元数据\r\n    const response = await fetch(httpUrl);\r\n    const metadata = await response.json();\r\n    console.log('NFT元数据:', metadata);\r\n  } catch (error) {\r\n    console.error('读取NFT信息时出错:', error);\r\n  }\r\n}\r\n\r\nmain();\r\n// // 主函数\r\n// async function main() {\r\n//   try {\r\n//     const owner = await getOwnerOf(tokenId)\r\n//     console.log(`NFT持有人地址: ${owner}`)\r\n\r\n//     const tokenURI = await getTokenURI(tokenId)\r\n//     console.log(`NFT元数据URI: ${tokenURI}`)\r\n\r\n//     // 如果需要进一步获取元数据，可以发起HTTP请求获取元数据\r\n//     const response = await fetch(tokenURI)\r\n//     const metadata = await response.json()\r\n//     console.log('NFT元数据:', metadata)\r\n//   } catch (error) {\r\n//     console.error('读取NFT信息时出错:', error)\r\n//   }\r\n// }\r\n\r\n// main()\r\n\r\n\r\n```\r\n\r\n然后重新运行\r\n\r\n```\r\npnpm start\r\n```\r\n\r\n结果如下：\r\n\r\n```\r\n@yhbdeMacBook-Air viem_nft1 % pnpm start\r\n\r\n> viem_nft1@1.0.0 start /Users/yhb/viem_nft/viem_nft1\r\n> node index.js\r\n\r\n(node:93006) ExperimentalWarning: Importing JSON modules is an experimental feature and might change at any time\r\n(Use `node --trace-warnings ...` to show where the warning was created)\r\nNFT持有人地址: 0x6897625C2Da7E985e9c22E0d7B27A960Fc81D1D2\r\nNFT元数据URI: ipfs://QmY9wa5FssaBBhLyyC2r649rwfS7CcvH7NG5AJWepeDkGj/1.json\r\n转换后的HTTP URL: https://ipfs.io/ipfs/QmY9wa5FssaBBhLyyC2r649rwfS7CcvH7NG5AJWepeDkGj/1.json\r\nNFT元数据: {\r\n  dna: 'b5940ead98dd50c06ccdb56ccfa37123ff083ca3',\r\n  name: 'ORBIT#1 Metaverse 1 Time and Space',\r\n  description: '',\r\n  image: 'ipfs://QmT3wMgcmm9R1dC1F63rBFdQdGYCfWfpv1D1PXYXTwrEaQ/1.jpg',\r\n  edition: 1,\r\n  date: 1715078282818,\r\n  attributes: [\r\n    { trait_type: 'Category', value: 'Metaverse' },\r\n    { trait_type: 'Color', value: 'Green' },\r\n    { trait_type: 'Planet', value: 'Moderate' },\r\n    { trait_type: 'Years', value: '2018' },\r\n    { trait_type: 'Characters', value: 'Null' },\r\n    { trait_type: 'Shapes', value: 'Square' },\r\n    { trait_type: 'Elements', value: 'Mixed' }\r\n  ]\r\n}\r\n```"},"author":{"user":"https://learnblockchain.cn/people/2184","address":"0xBb4445E9BAeD87aD6890f81f2a6A0394b417A3c7"},"history":"bafkreibzljfpr7rub2s2x2thzfbiwfpw2nh5rulatkzx3ym5qhfmwijtei","timestamp":1720835564,"version":1}