Virago: The Beginning
Updated on July 29 2022
Virago: The Beginning is the first Collection of 7,777 Viragos on the Ethereum Blockchain.
These Viragos are not only beautiful, but dangerous. The metaverse might be new but the Viragos have always existed. Where did they come from? What are they looking for? Did they come to help or did they come to harm us? Every question seems to lead to more mysteries.
First Pose of Virago NFTs
Second Pose of Virago NFTs
Third Pose of Virago NFTs

Traits

14 Backgrounds
20 Skin Colors
34 Clothes
45 Eyes
61 Hair Colors
26 Mouth
4 Necklace

Audit

MetaMilfs - Techrate Audit.pdf
371KB
PDF

Virago: The Beginning Contract

//Contract based on [https://docs.openzeppelin.com/contracts/3.x/erc721](https://docs.openzeppelin.com/contracts/3.x/erc721)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.12;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
contract MetaMilf is ERC721, ERC721Enumerable, Ownable {
uint public constant MAX_TOKENS = 7769;
uint public constant MAX_TOKENS_VIP = 8;
uint private _currentToken = 0;
uint public CURR_MINT_COST = 0.1 ether;
//---- Round based supplies
string private CURR_ROUND_NAME = "Presale";
uint private CURR_ROUND_SUPPLY = 2000;
uint private CURR_ROUND_TIME = 0;
uint private maxMintAmount = 2;
uint private nftPerAddressLimit = 2;
bytes32 private verificationHash = 0x3b25f67d4e97f5c0030ea90ca2882dbcefa05d1ef454bb67a77533172f4c6f38;
uint private currentVIPs = 0;
bool public hasSaleStarted = false;
bool public onlyWhitelisted = true;
string public baseURI;
uint256 private remaining = MAX_TOKENS;
mapping(uint256 => uint256) private cache;
constructor() ERC721("MetaMilfs", "METAMILFS") {
setBaseURI("http://api.metamilfs.io/metamilf/");
}
function totalSupply() public view override returns(uint) {
return _currentToken;
}
function _baseURI() internal view virtual override returns (string memory) {
return baseURI;
}
function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC721Enumerable) returns (bool)
{
return super.supportsInterface(interfaceId);
}
function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal override(ERC721, ERC721Enumerable) {
super._beforeTokenTransfer(from, to, tokenId);
}
function walletOfOwner(address _owner) public view returns (uint256[] memory)
{
uint256 ownerTokenCount = balanceOf(_owner);
uint256[] memory tokenIds = new uint256[](ownerTokenCount);
for (uint256 i; i < ownerTokenCount; i++) {
tokenIds[i] = tokenOfOwnerByIndex(_owner, i);
}
return tokenIds;
}
function drawIndex() private returns (uint256) {
uint256 i = uint(keccak256(abi.encodePacked(block.timestamp, msg.sender, remaining))) % remaining;
uint index = cache[i] == 0 ? i : cache[i];
index = index == 0 ? MAX_TOKENS : index;
cache[i] = cache[remaining - 1] == 0 ? remaining - 1 : cache[remaining - 1];
remaining = remaining - 1;
return index;
}
function mintNFT(uint _mintAmount, bytes32[] memory proof) external payable {
require(msg.value >= CURR_MINT_COST * _mintAmount, "Insufficient funds");
require(hasSaleStarted == true, "Sale hasn't started");
require(_mintAmount > 0, "Need to mint at least 1 NFT");
require(_mintAmount <= maxMintAmount, "Max mint amount per transaction exceeded");
require(_mintAmount <= CURR_ROUND_SUPPLY, "We're at max supply!");
require((_mintAmount + balanceOf(msg.sender)) <= nftPerAddressLimit, "Max NFT per address exceeded");
if(onlyWhitelisted == true) {
bytes32 user = keccak256(abi.encodePacked(msg.sender));
require(verify(user,proof), "User is not whitelisted");
}
for (uint256 i = 1; i <= _mintAmount; i++) {
_currentToken++;
CURR_ROUND_SUPPLY--;
uint theToken = drawIndex();
_safeMint(msg.sender, theToken);
}
}
function getInformations() external view returns (string memory, uint, uint, uint, uint,uint,uint, bool,bool)
{
return (CURR_ROUND_NAME,CURR_ROUND_SUPPLY,CURR_ROUND_TIME,CURR_MINT_COST,maxMintAmount,nftPerAddressLimit, _currentToken, hasSaleStarted, onlyWhitelisted);
}
function verify(bytes32 user, bytes32[] memory proof) internal view returns (bool)
{
bytes32 computedHash = user;
for (uint256 i = 0; i < proof.length; i++) {
bytes32 proofElement = proof[i];
if (computedHash <= proofElement) {
computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
} else {
computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
}
}
return computedHash == verificationHash;
}
//only owner functions
function setNewRound(uint _supply, uint cost, string memory name, uint perTransactionLimit, uint perAddressLimit, uint theTime, bool isOnlyWhitelisted, bool saleState) external onlyOwner {
require(_supply <= (MAX_TOKENS - _currentToken), "Exceeded supply");
CURR_ROUND_SUPPLY = _supply;
CURR_MINT_COST = cost;
CURR_ROUND_NAME = name;
maxMintAmount = perTransactionLimit;
nftPerAddressLimit = perAddressLimit;
CURR_ROUND_TIME = theTime;
hasSaleStarted = saleState;
onlyWhitelisted = isOnlyWhitelisted;
}
function setVerificationHash(bytes32 hash) external onlyOwner
{
verificationHash = hash;
}
function setOnlyWhitelisted(bool _state) external onlyOwner {
onlyWhitelisted = _state;
}
function setBaseURI(string memory _newBaseURI) public onlyOwner {
baseURI = _newBaseURI;
}
function reserveVIP(uint numTokens, address recipient) external onlyOwner {
require((currentVIPs + numTokens) <= MAX_TOKENS_VIP, "Exceeded VIP supply");
uint index;
for(index = 1; index <= numTokens; index++) {
_currentToken++;
currentVIPs = currentVIPs + 1;
uint theToken = currentVIPs + MAX_TOKENS;
_safeMint(recipient, theToken);
}
}
function Giveaways(uint numTokens, address recipient) external onlyOwner {
require((_currentToken + numTokens) <= MAX_TOKENS, "Exceeded supply");
uint index;
for(index = 1; index <= numTokens; index++) {
_currentToken++;
uint theToken = drawIndex();
_safeMint(recipient, theToken);
}
}
function withdraw(uint amount) external onlyOwner {
require(payable(msg.sender).send(amount));
}
function setSaleStarted(bool _state) external onlyOwner {
hasSaleStarted = _state;
}
}