Last active
October 25, 2022 17:02
-
-
Save adamlevoy/8e479aa7fc681ca2c5cfa1ba85f823de to your computer and use it in GitHub Desktop.
SgdKeys, an ERC721 contract for minting 100% on-chain SVG NFT
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.4; | |
import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; | |
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; | |
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; | |
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol"; | |
import "@openzeppelin/contracts/access/Ownable.sol"; | |
import "@openzeppelin/contracts/utils/Counters.sol"; | |
import "@openzeppelin/contracts/utils/Base64.sol"; | |
import "hardhat/console.sol"; | |
contract SgdKeys is ERC721, ERC721Enumerable, ERC721URIStorage, ERC721Burnable, Ownable { | |
using Counters for Counters.Counter; | |
Counters.Counter private _tokenIdCounter; | |
uint256 MAX_SUPPLY = 100; | |
constructor() ERC721("sgdKeys", "SGD") {} | |
event MintedNft(address sender, uint256 tokenId, string tokenUri); | |
event UpdatedTokenUri(uint256 tokenId, string tokenUri); | |
/** | |
* @dev svg code split into parts so variable inputs can be injected | |
*/ | |
string svgCodeOne = '<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" viewBox="0 0 500 500" fill="none"><g clip-path="url(#clip0_1_3)"><rect width="500" height="500" fill="white"/><rect width="500" height="500" fill="#1A1A1A"/><rect width="250" height="250" fill="#262626"/><rect x="250" y="250" width="250" height="250" fill="#262626"/><text transform="translate(24 424) rotate(-90)" fill="white" xml:space="preserve" style="white-space: pre" font-family="Moranga" font-size="36" font-weight="bold" letter-spacing="0.02em"><tspan x="0" y="38.2">'; | |
string svgCodeTwo = '</tspan></text><text transform="translate(79 424)" fill="white" xml:space="preserve" style="white-space: pre" font-family="Moranga" font-size="36" font-weight="bold" letter-spacing="0.02em"><tspan x="0" y="38.2">'; | |
string svgCodeThree = '</tspan></text></g><defs><clipPath id="clip0_1_3"><rect width="500" height="500" fill="white"/></clipPath></defs></svg>'; | |
/** | |
* @dev mints new token with user coordinates passed from frontend | |
* @param latitude user latitude via geolocation api | |
* @param longitude user longitude via geolocation api | |
*/ | |
function safeMint(string memory latitude, string memory longitude) public { | |
uint256 tokenId = _tokenIdCounter.current(); | |
uint256 currentSupply = totalSupply(); | |
require(currentSupply <= MAX_SUPPLY, "Sorry, all NFTs have been minted."); | |
_tokenIdCounter.increment(); | |
_safeMint(msg.sender, tokenId); | |
console.log("NFT minted with ID:", tokenId); | |
console.log("Latitude:", latitude); | |
console.log("Longitude:", longitude); | |
string memory finalSvgCode = string( | |
abi.encodePacked( | |
svgCodeOne, | |
latitude, | |
svgCodeTwo, | |
longitude, | |
svgCodeThree | |
) | |
); | |
string memory metaData = Base64.encode( | |
bytes( | |
string( | |
abi.encodePacked( | |
'{', | |
'"name": "sgd keys @ ', latitude, ' ', longitude,'",', | |
'"description": "A collection of keys to design, shoot, develop, and grow.",', | |
'"image": "data:image/svg+xml;base64,', Base64.encode(bytes(finalSvgCode)),'",', | |
'"latitude": "', latitude,'",', | |
'"longitude": "', longitude,'"' | |
'}' | |
) | |
) | |
) | |
); | |
string memory finalTokenUri = string( | |
abi.encodePacked( | |
"data:application/json;base64,", | |
metaData | |
) | |
); | |
_setTokenURI(tokenId, finalTokenUri); | |
emit MintedNft(msg.sender, tokenId, finalTokenUri); | |
} | |
/** | |
* @dev updates token uri based on token id, only owner can call | |
* @param latitude user latitude via owner input | |
* @param longitude user longitude via owner input | |
*/ | |
function updateTokenUri(uint256 _tokenId, string memory latitude, string memory longitude) public onlyOwner { | |
require(_exists(_tokenId), "Token does not exist."); | |
string memory finalSvgCode = string( | |
abi.encodePacked( | |
svgCodeOne, | |
latitude, | |
svgCodeTwo, | |
longitude, | |
svgCodeThree | |
) | |
); | |
string memory metaData = Base64.encode( | |
bytes( | |
string( | |
abi.encodePacked( | |
'{', | |
'"name": "sgd keys @ ', latitude, ' ', longitude,'",', | |
'"description": "A collection of keys to design, shoot, develop, and grow.",', | |
'"image": "data:image/svg+xml;base64,', Base64.encode(bytes(finalSvgCode)),'",', | |
'"latitude": "', latitude,'",', | |
'"longitude": "', longitude,'"' | |
'}' | |
) | |
) | |
) | |
); | |
string memory finalTokenUri = string( | |
abi.encodePacked( | |
"data:application/json;base64,", | |
metaData | |
) | |
); | |
_setTokenURI(_tokenId, finalTokenUri); | |
emit UpdatedTokenUri(_tokenId, finalTokenUri); | |
} | |
/** | |
* @dev increases max token supply by increment amount | |
* @param _increment amount to to increment max supply | |
*/ | |
function incMaxSupply(uint256 _increment) public onlyOwner { | |
MAX_SUPPLY = MAX_SUPPLY + _increment; | |
} | |
// The following functions are overrides required by Solidity. | |
function _beforeTokenTransfer(address from, address to, uint256 tokenId) | |
internal | |
override(ERC721, ERC721Enumerable) | |
{ | |
super._beforeTokenTransfer(from, to, tokenId); | |
} | |
function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) { | |
super._burn(tokenId); | |
} | |
function tokenURI(uint256 tokenId) | |
public | |
view | |
override(ERC721, ERC721URIStorage) | |
returns (string memory) | |
{ | |
return super.tokenURI(tokenId); | |
} | |
function supportsInterface(bytes4 interfaceId) | |
public | |
view | |
override(ERC721, ERC721Enumerable) | |
returns (bool) | |
{ | |
return super.supportsInterface(interfaceId); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment