// SPDX-License-Identifier: MIT /** ____ __ __ ____ ____ __ ____ ___ ( _ \( )( )( _ \( _ \( ) ( ___)/ __) ) _ < )(__)( ) _ < ) _ < )(__ )__) \__ \ (____/(______)(____/(____/(____)(____)(___/ HARBLINGER - FEBRUARY 2023 - BUBBLES (BBLS) 100% ON-CHAIN ANIMATED NFT **/ pragma solidity ^0.8.18; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; import "@openzeppelin/contracts/utils/Base64.sol"; contract Bubbles is ERC721, Ownable { using Strings for uint256; uint256 public constant MAX_TOKENS = 1888; uint256 public constant MAX_PER_TX = 20; uint256 public constant MAX_PER_WALLET = 100; uint public price = 88000000000000000000; // 88UBQ bool public isSaleActive; uint256 public totalSupply = 0; mapping(address => uint256) private mintedPerWallet; constructor() ERC721("BUBBLES", "BBLS") { for(uint256 i = 1; i <= 10; ++i) { // Pre-mint 10 _safeMint(msg.sender, i); } totalSupply = 10; } // Private Functions function getNumber(uint256 _rnd_num, uint256 _seed, uint256 _start, uint256 _end) private pure returns (uint256) { return ((_rnd_num ^ (_rnd_num >> _seed)) % (_end - _start)) + _start; } function getColor(uint256 _rnd_num, uint8 _seed) private pure returns (bytes memory) { return abi.encodePacked( "rgba(", getNumber(_rnd_num, _seed, 11, 255).toString(), ",", getNumber(_rnd_num, 1 + _seed, 11, 255).toString(), ",", getNumber(_rnd_num, 2 + _seed, 11, 255).toString(), ",0.", getNumber(_rnd_num, 3 + _seed, 10, 99).toString(), ")" ); } function getCircle(uint256 _rnd_num, uint8 _seed, int256 _xloc) private pure returns (bytes memory) { int256 x_offset = int256(_rnd_num ^ (_rnd_num >> _seed)) % 30; return abi.encodePacked( '', '', '', '' ); } // Public Functions function mint(uint256 _numTokens) external payable { require(isSaleActive, "Sale is paused"); require(_numTokens <= MAX_PER_TX, "Exceeds MAX_PER_TX"); require(mintedPerWallet[msg.sender] + _numTokens <= MAX_PER_WALLET, "Exceeds MAX_PER_WALLET"); uint256 curTotalSupply = totalSupply; require(curTotalSupply + _numTokens <= MAX_TOKENS, "Minted out"); require(_numTokens * price <= msg.value, "Insufficient funds"); for(uint256 i = 1; i <= _numTokens; ++i) { _safeMint(msg.sender, curTotalSupply + i); } mintedPerWallet[msg.sender] += _numTokens; totalSupply += _numTokens; } function tokenURI(uint256 _id) public view override returns (string memory) { require(totalSupply >= _id, "Not Minted Yet"); uint256 rnd_num = uint256(keccak256(abi.encodePacked(_id))); string memory svgData = Base64.encode(abi.encodePacked( '', getCircle(rnd_num, 7, 70), getCircle(rnd_num, 8, 150), getCircle(rnd_num, 9, 310), getCircle(rnd_num, 10, 450), '', 'Bubbles #', _id.toString(), '', '' )); bytes memory dataURI = abi.encodePacked( "{", '"name": "Bubbles #', _id.toString(), '",', '"description": "Bubbles: An animated on chain NFT",', '"image": "data:image/svg+xml;base64,', svgData, '"', "}" ); return string( abi.encodePacked( "data:application/json;base64,", Base64.encode(dataURI) ) ); } // Owner-only functions function toggleSaleState() external onlyOwner { isSaleActive = !isSaleActive; } function withdrawAll() external payable onlyOwner returns (bool) { (bool result,)= payable(msg.sender).call{value: address(this).balance }(""); return result; } }