// 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(
''
));
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;
}
}