

加密和NFT加密领域试图解决的一个大问题是中心化。区块链是其中一个重要部分。如果没有区块链,我们就不会有持有数百万美元的DAO或像CryptoPunks这样的项目。但是,大多数项目仍然存在一个问题:“如果IPFS下降怎么办?”
IPFS已经非常去中心化了,所以不用担心。但是一些收藏家可能更喜欢他们的代币的纯链上数据。元数据和艺术。值得庆幸的是,我们可以在 Solidity 中做到这一点。它也与 OpenSea 兼容。
如何在链上存储图像?
我们可以在链上存储任何东西,问题是如何存储它。我们很可能希望对我们的数据进行base64编码,并在其前缀/后缀上添加更多的信息,以便计算机知道它正在看什么。这就是OpenSea的工作原理,我已经进行了测试。
智能合约(或库)可用于将数据编码为链上 base64。我用过这个库。这是Loopring 团队的人做的。
现在,在针对NFT的智能合约中,我们希望能够将SVG数据编码为base64,并为其添加正确的信息前缀。
function svgToImageURI(string memory _source) public pure returns (string memory) { string memory baseURL = "data:image/svg+xml;base64,"; string memory svgBase64Encoded = Base64.encode(bytes(string(abi.encodePacked(_source)))); return string(abi.encodePacked(baseURL, svgBase64Encoded));}
这个函数将在后面的safeMint函数的另一个函数中使用,但它本质上是将SVG ‘ string ‘编码为base64,并将其编码后返回给Solidity以供进一步处理。
SVG不是字符串,它们是图像,对吧?
不,它们可以被视为图像,但它们只是被写成代码,非常类似于HTML。
图像看起来如何?
因为我之前已经对一个潜在的客户端进行了测试,所以我还找到了一个简单的SVG文件来进行测试。它在普通SVG中看起来是这样的:
<svg width=’500′ height=’500′ viewBox=’0 0 285 350′ fill=’none’ xmlns=’http://www.w3.org/2000/svg’> | |
---|---|
<path fill=’black’ d=’M150,0,L75,200,L225,200,Z’></path> | |
</svg> |
base64编码它看起来是这样的:
data:image/svg+xml;base64,PHN2ZyB3aWR0aD0nNTAwJyBoZWlnaHQ9JzUwMCcgdmlld0JveD0nMCAwIDI4NSAzNTAnIGZpbGw9J25vbmUnIHhtbG5zPSdodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2Zyc+PHBhdGggZmlsbD0nYmxhY2snIGQ9J00xNTAsMCxMNzUsMjAwLEwyMjUsMjAwLFonPjwvcGF0aD48L3N2Zz4=
我们可以复制这个已编码的字符串并将其粘贴到浏览器的地址栏中,以查看实际解析的SVG(至少在Chrome中是这样)。这就是我们正在做的,我们让它对浏览器具有可读性,除了我们需要让它对浏览器具有可访问性,而区块链这就是为什么我们用abi.encodePacked()对它进行进一步编码的原因。
注意字符串的第一个(粗体)。就是说data是一个imagefromsvg+xml编码的类型base64。

浏览器读取它没有任何问题
对 TokenURI 进行编码
TokenURI(通常)是指向带有特定属性的JSON文件的链接。但是,像图片一样,我们也可以对JSON文件进行base64编码,并使用正确的前缀使其在浏览器中可读。我们没有使用SVG前缀,而是使用data:application/json;base64来让浏览器知道它是一个base64编码的json文件。
我们也可以在Solidity中创建整个文件。我们把它编码,再编码,再编码。虽然这段代码看起来很有趣,但是很整洁,但它适用于OpenSea。
function formatTokenURI(string memory _imageURI, string memory _name, string memory _description, string memory _properties) public pure returns (string memory) { return string( abi.encodePacked( "data:application/json;base64,", Base64.encode( bytes( abi.encodePacked( '{"name":"', _name, '", "description": "', _description, '"', ', "attributes": ', _properties, ', "image":"', _imageURI, '"}' ) ) ) ) ); }
作为参数,我使用tokenURI具有的不同属性,并使用encodepack作为将所有字符串粘贴在一起的方法(因为JSON可以用作字符串)。
注意:_properties值是一个可以是数组的字符串,比如“[1,2,3]”。
我们使用导入的库将最终的JSON编码为base64,并再次使用encodepack为其添加前缀,使其可用于浏览器,就像它是一个常规URL一样。
ContractURI?
除了给出每个代币信息的TokenURI之外,我们还有ContractURI。它提供了关于收藏品的整体信息,OpenSea也(在一定程度上)支持这一点。除了合约地址,我们不需要在OpenSea中手动添加任何东西。
我用下面的代码编码它。它类似于TokenURI,但选项更少。我只是将整个ContractURI JSON作为一个字符串传递。
function setContractURI(string memory contractURI_) public onlyOwner() { _contractURI = string(abi.encodePacked( "data:application/json;base64,", Base64.encode( bytes( contractURI_ ) ) )); }
注意:OpenSea不会从区块链更新收集信息,所以在将其添加到OpenSea之前需设置一次!
铸造新代币
由于所有东西都在链上,所以铸造新的代币确实需要更多的信息。我们还需要设置所有信息(或稍后使用setTokenURI()函数进行设置),而不是简单地检查价格或创建一个新的代币。
function safeMint(string memory _source, string memory _name, string memory _description, string memory _properties) public onlyOwner() { _safeMint(msg.sender, _tokenIdCounter.current()); string memory imageURI = svgToImageURI(_source); _setTokenURI(_tokenIdCounter.current(), formatTokenURI(imageURI, _name, _description, _properties)); emit tokenChanged(_tokenIdCounter.current()); _tokenIdCounter.increment();}
因此,我们传递了更多的信息以及safeMint函数,如description、name、source (SVG)和properties,然后我们都使用前面介绍的函数对它们进行编码。
结论
尽管创建链上收藏品看起来很酷,但要小心,对于那些将制造代币的人来说,它是非常便宜的。我们必须向区块链提交更多的数据,这将消耗非常多的gas。
Source:https://medium.com/quick-programming/how-to-implement-fully-on-chain-nft-contracts-8c409acc98b7
关于
ChinaDeFi – ChinaDeFi.com 是一个研究驱动的DeFi创新组织,同时我们也是区块链开发团队。每天从全球超过500个优质信息源的近900篇内容中,寻找思考更具深度、梳理更为系统的内容,以最快的速度同步到中国市场提供决策辅助材料。
Layer 2道友 – 欢迎对Layer 2感兴趣的区块链技术爱好者、研究分析人与Gavin(微信: chinadefi)联系,共同探讨Layer 2带来的落地机遇。敬请关注我们的微信公众号 “去中心化金融社区”。
