You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

128 lines
5.1 KiB
Solidity

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {ERC721} from "solmate/tokens/ERC721.sol";
import {Owned} from "solmate/auth/Owned.sol";
import {LibString} from "solmate/utils/LibString.sol";
import {Punkhunt} from "./Punkhunt.sol";
/**
@title Duck
@author lzamenace.eth
@notice This contract contains ERC-721 Duck tokens (DUCK) which are used as
utility tokens for the Punkhunt NFT project and chain based game.
@dev All contract functions regarding token burning and minting are limited to
the Punkhunt interface where the logic and validation resides.
*/
contract Duck is ERC721, Owned {
using LibString for uint256;
/// Track mints per wallet to enforce maximum
mapping(address => uint256) public tokensMintedByWallet;
/// Maximum amount of survivors repunkhunting
uint256 public constant MAX_SURVIVOR_COUNT = 1;
/// Maximum amount of mints per wallet
uint256 public constant MAX_MINT_AMOUNT = 50;
/// Amount of Ducks killed (tokens burned)
uint256 public burned;
/// Amount of Ducks minted
uint256 public minted;
/// Base URI for Duck image
string public baseURI;
/// Whether or not minting is active (timed edition)
bool public mintingActive;
/// Contract address of the deployed Punkhunt contract interface to the game
Punkhunt public punkhunt;
constructor() ERC721("Duck", "DUCK") Owned(msg.sender) {}
// =========================================================================
// Admin
// =========================================================================
/// Set metadata URI for Duck PFPs and explosions
/// @param _baseURI IPFS hash or URL to retrieve JSON metadata for living Duck tokens
function setBaseURI(string calldata _baseURI) external onlyOwner {
baseURI = _baseURI;
}
/// Set punkhunt contract address for executing functions
/// @param _address Contract address of the deployed Punkhunt contract
function setPunkhuntContract(address _address) external onlyOwner {
punkhunt = Punkhunt(_address);
}
/// Toggle minting of DUCK (open edition)
function toggleMinting() external onlyOwner {
mintingActive = !mintingActive;
}
// =========================================================================
// Modifiers
// =========================================================================
/// Limit function execution to deployed Punkhunt contract
modifier onlyPunkhunt {
require(msg.sender == address(punkhunt), "invalid msg sender");
_;
}
// =========================================================================
// Tokens
// =========================================================================
/// Helper function to get supply minted
/// @return supply Number of Ducks alive
function totalSupply() public view returns (uint256) {
return minted - burned;
}
/// Mint tokens from punkhunt contract
/// @param _to Address to mint DUCK tokens to
/// @param _amount Amount of DUCK tokens to mint
function mint(address _to, uint256 _amount) external onlyPunkhunt {
require(mintingActive, "minting not active");
require(tokensMintedByWallet[_to] + _amount <= MAX_MINT_AMOUNT, "cannot exceed maximum per wallet");
for (uint256 i; i < _amount; i++) {
minted++;
_safeMint(_to, minted);
}
tokensMintedByWallet[_to] += _amount;
}
/// Toggle token state from living to dead
/// @param _tokenId Token ID of DUCK to toggle living -> dead and increment kill count
function die(uint256 _tokenId) external onlyPunkhunt {
require(_tokenId <= minted, "invalid token id");
if (ownerOf(_tokenId) != address(0)) {
burned++;
_burn(_tokenId);
}
}
/// Retrieve owner of given token ID
/// @param _tokenId Token ID to check owner of
/// @return owner Address of owner
/// @dev Overridden from Solmate contract to allow zero address returns
function ownerOf(uint256 _tokenId) public view override returns (address owner) {
return _ownerOf[_tokenId];
}
// Return URI to retrieve JSON metadata from - points to images and descriptions
/// @param _tokenId Token ID of DUCK to fetch URI for
/// @return string IPFS or HTTP URI to retrieve JSON metadata from
function tokenURI(uint256 _tokenId) public view override returns (string memory) {
if (ownerOf(_tokenId) == address(0)) {
return string(abi.encodePacked(baseURI, "dead.json"));
} else {
return string(abi.encodePacked(baseURI, "alive.json"));
}
}
/// Checks if contract supports a given interface
/// @param interfaceId The interface ID to check if contract supports
/// @return bool Boolean value if contract supports interface ID or not
function supportsInterface(bytes4 interfaceId) public view virtual override (ERC721) returns (bool) {
return super.supportsInterface(interfaceId);
}
}