# 使用opensea伪造NFT **Published by:** [plsdm7](https://paragraph.com/@dogsuisui/) **Published on:** 2022-12-19 **URL:** https://paragraph.com/@dogsuisui/opensea-nft ## Content https://app.pinata.cloud/pinmanager 使用openzepplin部署NFT合约// SPDX-License-Identifier: MIT pragma solidity ^0.8.9; import "@openzeppelin/contracts@4.5.0/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts@4.5.0/token/ERC721/extensions/ERC721Enumerable.sol"; import "@openzeppelin/contracts@4.5.0/access/Ownable.sol"; import "@openzeppelin/contracts@4.5.0/security/ReentrancyGuard.sol"; import "@openzeppelin/contracts@4.5.0/utils/cryptography/ECDSA.sol"; contract WarriorERC721 is ERC721("Galxe Stargator Warrior NFTs", ""), ERC721Enumerable, Ownable, ReentrancyGuard { string private _baseURIextended; string private _baseURISuffix = ""; bool public frozenMetadata = false; uint256 public MAX_TOKENS = 10000; bool public saleIsActive = false; bool public greenListSaleIsActive = false; mapping(address => uint256) addressToGreenListNumberMinted; mapping(address => uint256) addressToNumberMinted; address private bouncer = 0xd47257ee6cD0a80Ed1ba7bDdC722Ef8eE44a8d7d; bool private _baseUriIncludeTokenId = true; function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal override(ERC721, ERC721Enumerable) { super._beforeTokenTransfer(from, to, tokenId); } function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, ERC721Enumerable) returns (bool) { return super.supportsInterface(interfaceId); } function flipSaleState() public onlyOwner { saleIsActive = !saleIsActive; } function flipGreenListSaleState() public onlyOwner { greenListSaleIsActive = !greenListSaleIsActive; } function setBaseURI(string memory baseURI_, bool includeTokenId, string memory suffix) external onlyOwner() { require(!frozenMetadata, "The metadata URI is frozen. You can no longer set the baseURI"); _baseUriIncludeTokenId = includeTokenId; _baseURIextended = baseURI_; _baseURISuffix = suffix; } function _baseURI() internal view virtual override returns (string memory) { return _baseURIextended; } function freezeMetadata() external onlyOwner { frozenMetadata = true; } function showBaseURI() public view returns (string memory) { return _baseURIextended; } 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)) : ""; // if (_baseUriIncludeTokenId) { // return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, Strings.toString(tokenId), _baseURISuffix)) : ""; // } else { // return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI)) : ""; // } } function mintToken(uint numberOfTokens) public payable nonReentrant { require(tx.origin == _msgSender(), "This function is only callable from an EOA."); require(saleIsActive, "Sale must be active to mint Tokens"); require(numberOfTokens + addressToNumberMinted[_msgSender()] <= 10000, "Exceeded max token purchase"); require(totalSupply() + numberOfTokens <= MAX_TOKENS, "Purchase would exceed max supply of tokens"); require(0 * numberOfTokens <= msg.value, "Ether value sent is not correct"); for (uint i = 0; i < numberOfTokens; i++) { uint mintIndex = totalSupply(); if (totalSupply() < MAX_TOKENS) { addressToNumberMinted[_msgSender()] += 1; _safeMint(_msgSender(), mintIndex); } } } function greenListMintToken(uint numberOfTokens, bytes memory signature) public payable nonReentrant { require(tx.origin == _msgSender(), "This function is only callable from an EOA."); require(greenListSaleIsActive, "Sale must be active to mint Tokens"); require(numberOfTokens + addressToGreenListNumberMinted[_msgSender()] <= 10000, "Exceeded max token purchase"); require(totalSupply() + numberOfTokens <= MAX_TOKENS, "Purchase would exceed max supply of tokens"); require(0 * numberOfTokens <= msg.value, "Ether value sent is not correct"); bytes32 hashedMinter = ECDSA.toEthSignedMessageHash(keccak256(abi.encodePacked(_msgSender()))); address recoveredBouncer = ECDSA.recover(hashedMinter, signature); require(recoveredBouncer == bouncer, "The signature for the greenlist is invalid"); for (uint i = 0; i < numberOfTokens; i++) { uint mintIndex = totalSupply(); if (totalSupply() < MAX_TOKENS) { addressToGreenListNumberMinted[_msgSender()] += 1; _safeMint(_msgSender(), mintIndex); } } } function reserveTokens(uint quantity, address recipient) public onlyOwner { uint supply = totalSupply(); require(supply + quantity <= MAX_TOKENS, "We have already hit the reserve limit"); uint i; for (i = 0; i < quantity; i++) { uint mintIndex = totalSupply(); _safeMint(recipient, mintIndex); } } } 这个应该是quix launchpad的部分代码。 使用pinata生成ipfs,这里注意一下现在穿一个json里面配置好资源信息(重要的就是名字和图片地址了),如果所有图片都一样,那就传一个就可以了。如果每个需要单独配,可以看下合约代码,里面有关于index的部分。 对于所有NFT图片都一样的,可以如合约里直接写死baseURI,并且可以预留一个接口用来修改baseURI。 我开始还有个想法,就是用一个合约通过设置baseURI达到复用效果,但是这样的话需要把所有mint过的id都设置一遍,这个gas太高了所以放弃。 在市场上发布有几个注意点: 1、NFT的名字是唯一的,所以可以尽量写和原系列相似的 2、quix上可以添加表情符号,比如这个符号✅,懂得都懂 3、NFT类别可以多选几个,容易被搜索到 4、非常适合官方盲盒刚mint但是还不能开并且名字图片都一样的(因为id不一样配置文件要多个),基本做到一模一样。 5、成本就是部署合约(上面合约应该可以再简化例如全部写成onlyowner然后判断啥的都去了)和mint。现在polygon最合适的链,成本非常低可以忽略而且有很多热度项目。 6、OS上有很严格的审查程序,我试着改了几个热门项目的名字,然后修改元数据图片,没多久就被封号了。所以机会可能还是在新出的项目(比如刚mint的)这种OS不可能会很快审查。其他平台暂时没发现这种 ## Publication Information - [plsdm7](https://paragraph.com/@dogsuisui/): Publication homepage - [All Posts](https://paragraph.com/@dogsuisui/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@dogsuisui): Subscribe to updates - [Twitter](https://twitter.com/plsdm7): Follow on Twitter