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.
132 lines
5.6 KiB
Solidity
132 lines
5.6 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 {Main} from "./Main.sol";
|
|
|
|
/**
|
|
@title Unaboomer
|
|
@author 0xgrimey.eth
|
|
@notice This contract contains ERC-721 Unaboomer tokens (BOOMR) which are the profile
|
|
picture and membership tokens for the Unaboomer NFT project and chain based game.
|
|
Each Unaboomer is a unique, dynamically generated pixel avatar in the likeness
|
|
of the real-life Unabomber, Theodore Kaczynski. Unaboomers can be "killed" by
|
|
other players by "sending" (burning) mailbombs. When Unaboomers are killed their
|
|
corresponding image is replaced with an explosion, rendering it worthless as any
|
|
rarity associated with it ceases to exist. The game stops when SURVIVOR_COUNT
|
|
threshold is breached. The surviving players (any address which holds an "alive"
|
|
Unaboomer) will advance to the next round of gameplay.
|
|
@dev All contract functions regarding token burning and minting are limited to
|
|
the Main interface where the logic and validation resides.
|
|
*/
|
|
contract Unaboomer is ERC721, Owned {
|
|
using LibString for uint256;
|
|
|
|
/// Track if a BOOMR token is toggled as alive or dead
|
|
mapping(uint256 => bool) public tokenDead;
|
|
/// Maximum supply of BOOMR tokens
|
|
uint256 public constant MAX_SUPPLY = 10000;
|
|
/// Maximum amount of survivors remaining to advance to the next round
|
|
uint256 public constant SURVIVOR_COUNT = 1000;
|
|
/// The total amount of Unaboomers who have been killed during the game
|
|
uint256 public totalKillCount;
|
|
/// Number of tokens minted (total supply)
|
|
uint256 public minted;
|
|
/// Base URI for living Unaboomers - original pixelated avatars
|
|
string public aliveURI;
|
|
/// Base URI for dead Unaboomers - pixelated explosion
|
|
string public deadURI;
|
|
/// Contract address of the deployed Main contract interface to the game
|
|
Main public main;
|
|
|
|
constructor() ERC721("Unaboomer", "BOOMR") Owned(msg.sender) {}
|
|
|
|
// =========================================================================
|
|
// Admin
|
|
// =========================================================================
|
|
|
|
/// Withdraw funds to contract owner
|
|
function withdraw() external onlyOwner {
|
|
uint256 balance = address(this).balance;
|
|
payable(msg.sender).transfer(balance);
|
|
}
|
|
|
|
/// Set metadata URI for living Unaboomer tokens
|
|
/// @param _baseURI IPFS hash or URL to retrieve JSON metadata for living Unaboomer tokens
|
|
function setAliveURI(string calldata _baseURI) external onlyOwner {
|
|
aliveURI = _baseURI;
|
|
}
|
|
|
|
/// Set metadata URI for dead Unaboomer tokens
|
|
/// @param _baseURI IPFS hash or URL to retrieve JSON metadata for dead Unaboomer tokens
|
|
function setDeadURI(string calldata _baseURI) external onlyOwner {
|
|
deadURI = _baseURI;
|
|
}
|
|
|
|
/// Set main contract address for executing functions
|
|
/// @param _address Contract address of the deployed Main contract
|
|
function setMainContract(address _address) external onlyOwner {
|
|
main = Main(_address);
|
|
}
|
|
|
|
// =========================================================================
|
|
// Modifiers
|
|
// =========================================================================
|
|
|
|
/// Limit function execution to deployed Main contract
|
|
modifier onlyMain {
|
|
require(msg.sender == address(main), "invalid msg sender");
|
|
_;
|
|
}
|
|
|
|
// =========================================================================
|
|
// Tokens
|
|
// =========================================================================
|
|
|
|
/// Helper function to get supply minted
|
|
/// @return supply Number of Unaboomers radicalized in totality (minted)
|
|
function totalSupply() public view returns (uint256) {
|
|
return minted;
|
|
}
|
|
|
|
/// Mint tokens from main contract
|
|
/// @param _to Address to mint BOOMR tokens to
|
|
/// @param _amount Amount of BOOMR tokens to mint
|
|
function radicalize(address _to, uint256 _amount) external payable onlyMain {
|
|
require(totalSupply() + _amount <= MAX_SUPPLY, "supply reached");
|
|
for (uint256 i; i < _amount; i++) {
|
|
_safeMint(_to, minted);
|
|
minted++;
|
|
}
|
|
}
|
|
|
|
/// Toggle token state from living to dead
|
|
/// @param tokenId Token ID of BOOMR to toggle living -> dead and increment kill count
|
|
function die(uint256 tokenId) external onlyMain {
|
|
require(tokenId < totalSupply(), "invalid token id");
|
|
if (tokenDead[tokenId] == false) {
|
|
totalKillCount++;
|
|
tokenDead[tokenId] = true;
|
|
}
|
|
}
|
|
|
|
// Return URI to retrieve JSON metadata from - points to images and descriptions
|
|
/// @param _tokenId Token ID of BOOMR 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 (tokenDead[_tokenId] == true) {
|
|
return string(abi.encodePacked(deadURI, _tokenId.toString(), ".json"));
|
|
} else {
|
|
return string(abi.encodePacked(aliveURI, _tokenId.toString(), ".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);
|
|
}
|
|
} |