Skip to content

Instantly share code, notes, and snippets.

@defijesus
Last active July 30, 2022 11:18
Show Gist options
  • Save defijesus/92316bb52b14996b52679e777c43a0ee to your computer and use it in GitHub Desktop.
Save defijesus/92316bb52b14996b52679e777c43a0ee to your computer and use it in GitHub Desktop.
// SPDX-License-Identifier: MIT
// t11s god himself
// βœ… make mintPrice constant
// πŸ”΄ consider removing creators
// πŸ”΄ consider removing ourMessage
// βœ… make heartURIs a mapping of uint256s
// βœ… don't use OZ counters
// βœ… dont use a uint8 for the loop counter (its gonna get cast to 256 bits as thats the evm wordsize)
// βœ… dont cast to a uint8 for digit
// πŸ”΄ the "slow down" ratelimit in mint is basically useless, consider removing
// βœ… don't use uint8 for loop in mint
// βœ… dont use uint8 for loop in setHeartURIs
// βœ… don't use uint8 for loop in setCustomURIs
// βœ… make maxSupply constant
// βœ… precompute the empty hash in a constant for setCustomURIs (keccak256(abi.encodePacked("")))
// βœ… make all arrays args in external funcs `calldata`
// βœ… wrap all ur loop counters in unchecked
// βœ… rule of thumb: always calldata if the compiler will let u
//
// My attempt
// βœ… add custom errors
// βœ… converted creators string[] to string
pragma solidity ^0.8.2;
import "@openzeppelin/contracts@4.4.0/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts@4.4.0/security/Pausable.sol";
import "@openzeppelin/contracts@4.4.0/access/Ownable.sol";
error NonExistantToken();
error SlowDown();
error CorrectChangeOnly();
error TooMany();
error WrongArraySize();
contract CryptoHeart is ERC721, Pausable, Ownable {
// these 2 have to stay
string public constant CREATORS = "defijesus.eth+gbaby.eth+jpegmedia.eth+kingkav.eth";
string public constant README = "People with disabilities have helped us gain a better perspective on life. I hope this project does the same for you.";
uint256 public constant mintPrice = 50000000000000000;
bytes32 private constant emptyString = keccak256(abi.encodePacked(""));
// mapping is cheaper than string[]
mapping(uint256 => string) public heartURIs;
// token ID => custom token URI
mapping(uint256 => string) public customURIs;
uint256 public constant maxSupply = 1000;
uint256 public currentSupply = 0;
constructor(string[] memory _heartURIs) ERC721("CryptoHeart", "HEART") {
unchecked {
for (uint256 i = 0; i < 10; i++) {
heartURIs[i] = _heartURIs[i];
safeMint(msg.sender);
}
}
pause();
}
// CUSTOM FUNCTIONS
// PUBLIC
function tokenURI(uint256 tokenId) public view override returns (string memory) {
if (!_exists(tokenId)) {
revert NonExistantToken();
}
if (keccak256(abi.encodePacked(customURIs[tokenId])) != emptyString) {
return customURIs[tokenId];
}
uint256 digit = uint256(tokenId % 10);
return heartURIs[digit];
}
function mint(uint256 amount) external payable whenNotPaused {
if (amount > 20) {
revert SlowDown();
} else if (msg.value != mintPrice * amount) {
revert CorrectChangeOnly();
} else if (currentSupply + amount > maxSupply) {
revert TooMany();
}
unchecked {
for(uint256 i = 0; i < amount; i++) {
safeMint(msg.sender);
}
}
}
// ONLY OWNER
function setHeartURIs(string[] calldata URIs) external onlyOwner {
if (URIs.length != 10) {
revert WrongArraySize();
}
unchecked {
for (uint256 i = 0; i < URIs.length; i++) {
heartURIs[i] = URIs[i];
}
}
}
function setCustomURIs(uint256[] calldata tokenIDs, string[] calldata tokenURIs) external onlyOwner {
if (tokenIDs.length != tokenURIs.length) {
revert WrongArraySize();
}
unchecked {
for (uint256 i = 0; i < tokenIDs.length; i++) {
if (keccak256(abi.encodePacked(tokenURIs[i])) == emptyString) {
delete customURIs[tokenIDs[i]];
} else {
customURIs[tokenIDs[i]] = tokenURIs[i];
}
}
}
}
function withdrawEther(address payable to) external onlyOwner {
to.call{value: address(this).balance}("");
}
function safeMint(address to) internal {
_safeMint(to, currentSupply);
currentSupply += 1;
}
// DEFAULT FUNCTIONS
function pause() public onlyOwner {
_pause();
}
function unpause() public onlyOwner {
_unpause();
}
function _beforeTokenTransfer(address from, address to, uint256 tokenId)
internal
whenNotPaused
override
{
super._beforeTokenTransfer(from, to, tokenId);
}
}
@alephao
Copy link

alephao commented Dec 11, 2021

small stuff but might help πŸ€·β€β™‚οΈ

  1. You only use mintPrice and maxSupply once, just use the raw value instead of declaring a constants (save around 50gas on each lol)
  2. On uint256 digit = uint256(tokenId % 10); no need cast to uint256
  3. When looping through mint, change the currentSupply only once instead of incrementing every time (First loop waste ~130 more gas, after that save ~250 gas per loop) (example below)

Optimized mint loop example (when amount is >=2):

for (uint256 i = 0; i < amount; i++) {
  _safeMint(msg.sender, currentSupply+i);
}
currentSupply += amount;

Edit: unnecessary mem store (save ~22 gas)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment