// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/utils/Context.sol";
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) internal _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 internal _totalSupply;
uint8 internal constant _decimals = 9;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public pure returns (uint8) {
return _decimals;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(
address account
) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(
address to,
uint256 amount
) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(
address owner,
address spender
) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(
address spender,
uint256 amount
) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(
address from,
address to,
uint256 amount
) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(
address spender,
uint256 addedValue
) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(
address spender,
uint256 subtractedValue
) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(
currentAllowance >= subtractedValue,
"ERC20: decreased allowance below zero"
);
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(
address from,
address to,
uint256 amount
) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
uint256 fromBalance = _balances[from];
require(
fromBalance >= amount,
"ERC20: transfer amount exceeds balance"
);
unchecked {
_balances[from] = fromBalance - amount;
// Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
// decrementing then incrementing.
_balances[to] += amount;
}
emit Transfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_totalSupply += amount;
unchecked {
// Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
}
/**
* @dev Erases `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
// Overflow not possible: amount <= accountBalance <= totalSupply.
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(
address owner,
address spender,
uint256 amount
) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(
address owner,
address spender,
uint256 amount
) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(
currentAllowance >= amount,
"ERC20: insufficient allowance"
);
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./lib/Ownable.sol";
uint constant levelsCount = 5;
uint constant bcgroundsCount = 6;
uint constant groundsCount = 1;
uint8 constant pixelsCount = 24;
uint constant seedLevel1 = 1000; // not actually used
uint constant seedLevel2 = 21000; // 0.01%
uint constant seedLevel3 = 105000; // 0.05%
uint constant seedLevel4 = 420000; // 0.2%
uint constant seedLevel5 = 1050000; // 0.5%
struct MedusaData {
uint lvl;
string background;
string background2;
bool hasGround;
uint ground;
string groundColor;
bool hasBubble;
uint bubble;
string bubbleColor;
bool hasWeed;
uint weed;
string weedColor;
uint mirrorTime;
uint bobTime;
uint tentacle;
string tentacleColor;
uint bell;
string bellColor;
// bool hasDots;
// string dotsColor;
}
struct Rect {
uint8 x;
uint8 y;
uint8 width;
uint8 height;
}
struct FileData {
uint lvl;
uint file;
Rect[] rects;
}
struct ColorsData {
string[] lvl0;
string[] lvl1;
string[] lvl2;
string[] lvl3;
string[] lvl4;
}
struct SeedData {
uint seed;
uint extra;
}
struct Rand {
uint seed;
uint nonce;
uint extra;
}
library RandLib {
function next(Rand memory rnd) internal pure returns (uint) {
return
uint(
keccak256(
abi.encodePacked(rnd.seed + rnd.nonce++ - 1, rnd.extra)
)
);
}
function lvl(Rand memory rnd) internal pure returns (uint) {
if (rnd.seed < seedLevel1) return 0;
if (rnd.seed < seedLevel2) return 1;
if (rnd.seed < seedLevel3) return 2;
if (rnd.seed < seedLevel4) return 3;
if (rnd.seed < seedLevel5) return 4;
return 5;
}
function random(
string[] memory data,
Rand memory rnd
) internal pure returns (string memory) {
return data[randomIndex(data, rnd)];
}
function randomIndex(
string[] memory data,
Rand memory rnd
) internal pure returns (uint) {
return next(rnd) % data.length;
}
}
library LayersLib {
function setLayers(
mapping(uint => mapping(uint => Rect[])) storage rects,
FileData[] calldata data
) internal {
for (uint i = 0; i < data.length; ++i) {
setFile(rects, data[i]);
}
}
function setFile(
mapping(uint => mapping(uint => Rect[])) storage rects,
FileData calldata input
) internal {
Rect[] storage storageFile = rects[input.lvl][input.file];
for (uint i = 0; i < input.rects.length; ++i) {
storageFile.push(input.rects[i]);
}
}
function getLvl(
mapping(uint => mapping(uint => Rect[])) storage rects,
uint lvl
) internal view returns (mapping(uint => Rect[]) storage) {
return rects[lvl];
}
function to_lvl_1(uint l) internal pure returns (uint) {
if (l > 0) --l;
return l;
}
}
library Converter {
function toString(uint value) internal pure returns (string memory) {
if (value == 0) {
return "0";
}
uint temp = value;
uint digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
}
library RectLib {
using RectLib for Rect;
using RandLib for Rand;
using RandLib for string[];
using Converter for uint8;
function toSvg(
Rect memory r,
string memory color
) internal pure returns (string memory) {
return
string(
abi.encodePacked(
"<rect x='",
r.x.toString(),
"' y='",
r.y.toString(),
"' width='",
r.width.toString(),
"' height='",
r.height.toString(),
"' fill='",
color,
"'/>"
)
);
}
function toSvg(
Rect[] storage rects,
string[] storage colors,
Rand memory rnd
) internal view returns (string memory) {
string memory res;
for (uint i = 0; i < rects.length; ++i) {
res = string(
abi.encodePacked(res, rects[i].toSvg(colors.random(rnd)))
);
}
return res;
}
function toSvg(
Rect[] storage rects,
string memory color
) internal view returns (string memory) {
string memory res;
for (uint i = 0; i < rects.length; ++i) {
res = string(abi.encodePacked(res, rects[i].toSvg(color)));
}
return res;
}
}
contract Generator is Ownable {
using LayersLib for mapping(uint => mapping(uint => Rect[]));
using LayersLib for mapping(uint => string[]);
using LayersLib for uint;
using RectLib for Rect;
using RectLib for Rect[];
using RandLib for Rand;
using RandLib for string[];
using Converter for uint;
// uint8 polyps_count = 7;
uint8[levelsCount] weedLevelCounts = [7, 7, 7, 7, 7];
uint8[levelsCount] bubbleLevelCounts = [8, 8, 8, 8, 8];
uint8[levelsCount] tentacleLevelCounts = [5, 5, 5, 6, 10];
uint8[levelsCount] bellLevelCounts = [7, 10, 10, 10, 10];
// uint8[levelsCount] dotLevelCounts = [5, 7, 10, 10, 10];
// mapping(uint => Rect[]) polyps;
mapping(uint => mapping(uint => Rect[])) weeds;
mapping(uint => mapping(uint => Rect[])) bubbles;
mapping(uint => mapping(uint => Rect[])) tentacles;
mapping(uint => mapping(uint => Rect[])) bells;
// mapping(uint => mapping(uint => Rect[])) dots;
mapping(uint => Rect[]) grounds;
string[] private backgroundColors0 = [
"#CC93B1",
"#E0BCA3",
"#DDAFB7",
"#A9D8C5",
"#72C7CC",
"#DBC9C9",
"#D6CAA0",
"#9ED3CB",
"#4876AF",
"#95C4B7",
"#BDC19E",
"#50B6BF",
"#D3BAD3"
];
string[] private backgroundColors1 = [
"#B0E8BE",
"#76E2D0",
"#D8CE8F",
"#F7C5C5",
"#DCEA96",
"#BF8FD6",
"#EABFB9",
"#E0D6BA",
"#8B96DD",
"#C2EFDF",
"#B1E0E0",
"#B6D99B",
"#FFD8DD"
];
string[] private backgroundColors2 = [
"#96D1CB",
"#65CEC2",
"#43CC93",
"#59DBCD",
"#86C0D8",
"#958DD6",
"#B0E8BE",
"#D3B3CD",
"#8AA8CC",
"#77E5DA",
"#7D91E0",
"#E8BECD",
"#BFDBB3"
];
string[] private backgroundColors3 = [
"#FFC300",
"#FFCF99",
"#FFBFC1",
"#FFBCC8",
"#6DC2CA",
"#3AA3D3",
"#D18188",
"#D3A5D1",
"#F6FFDD",
"#FFE4C0",
"#FFA0BB",
"#BD97D8",
"#2B2582",
"#6DA4CA"
];
string[] private backgroundColors4 = [
"#FFB2EB",
"#A3F7D5",
"#3A85FF",
"#FFC6F2",
"#FFB26D",
"#D000FF",
"#FFFF21",
"#FF7716",
"#FF46CE",
"#84FCFF",
"#00FFCB",
"#BDA5FF",
"#4FC1FF",
"#59B4FF",
"#7FFF7F",
"#A8FFF9",
"#BFFFE6",
"#71C692",
"#854AC4",
"#3C00C1",
"#3C81C1",
"#FF4473",
"#D6B7FF",
"#FFC4A5",
"#8E7CFF"
];
string[] private backgroundColors42 = [
// "#FFB2EB",
"#A3F7D5",
"#3A85FF",
"#FFC6F2",
"#FFB26D",
"#D000FF",
"#FFFF21",
"#FF7716",
"#FF46CE",
"#84FCFF",
"#00FFCB",
"#BDA5FF",
"#4FC1FF",
"#59B4FF",
"#7FFF7F",
"#A8FFF9",
"#BFFFE6",
"#71C692",
"#854AC4",
"#3C00C1",
"#3C81C1",
"#FF4473",
"#D6B7FF",
"#FFC4A5",
"#8E7CFF"
];
string[] private bubbleColors = [
"#A5FFF1",
"#FFB26B",
"#F3FFD6",
"#D4D8C9",
"#F5E0FF",
"#9F9BA0",
"#D1F1FF",
"#E2F1FF",
"#FFFFFF",
"#FF77D6",
"#C1FFC8",
"#FFE595",
"#E8FFE8",
"#F5E0FF",
"#FFE4A7",
"#F5E0FF"
];
string[] private groundColors0 = [
"#9B5B71",
"#5B9B7E",
"#9E8640",
"#DD8C7A",
"#478C7A",
"#CC9380",
"#4CA188",
"#B777A2",
"#0077A2",
"#AD9474",
"#7CAFA5",
"#A08063",
"#6A5D77",
"#436E7F",
"#428255"
];
string[] private groundColors1 = [
"#CC8F7E",
"#77531D",
"#7BBC45",
"#23756B",
"#85A094",
"#DBAF84",
"#BC8D97",
"#7CBA9A",
"#5460A8",
"#C65B86",
"#CC94BC",
"#C4BA00",
"#436E7F",
"#AB72C1"
];
string[] private groundColors2 = [
"#D7E29E",
"#C3E074",
"#C3E074",
"#5267A8",
"#90CCB4",
"#C3E074",
"#90CCB4",
"#FFD477",
"#CC6394",
"#CCA75D",
"#CCA7BA",
"#CC694B",
"#A59578",
"#FFC47C",
"#755D33",
"#E8B534"
];
string[] private groundColors3 = [
"#E587A0",
"#FF664F",
"#EAA4CA",
"#FFC47C",
"#6D88CA",
"#D8A488",
"#632577",
"#6F7552",
"#2C7FB7",
"#A85E88",
"#ADA35A",
"#77625F",
"#E0AE8F",
"#8CB773",
"#D3B16E",
"#11A062",
"#83969E",
"#B5833D",
"#FFFFFF"
];
string[] private medusaColors0 = [
"#BC646B",
"#BC646B",
"#8D5BA3",
"#D3CFA0",
"#D170B1",
"#50B2D8",
"#90C46F",
"#856ED3",
"#C471B2",
"#C4712F",
"#BDC1BF",
"#C67584",
"#74AA78",
"#A85291",
"#BF96DD",
"#DB7D81",
"#E59B87",
"#017CBF"
];
string[] private medusaColors1 = [
"#FF9B2F",
"#5E33E0",
"#7C3374",
"#CEE25D",
"#12A9CC",
"#E29CA7",
"#A67FE0",
"#D86B65",
"#5ABC79",
"#6DA1BA",
"#913E49",
"#FF6D81",
"#DBAF00",
"#A8AEFF",
"#E59B87",
"#635158",
"#D8505D",
"#C33374"
];
string[] private medusaColors2 = [
"#7CFF83",
"#0093FF",
"#96FFF4",
"#ECFFBF",
"#F26979",
"#FFE1DF",
"#A793B2",
"#F2E479",
"#B1E0C9",
"#D3FF66",
"#36E2CE",
"#E07CF0",
"#5E33E0",
"#FF77A4",
"#8D80DD",
"#AFCF93",
"#A8AEFF"
];
string[] private medusaColors3 = [
"#FF71D3",
"#D36DE5",
"#3AE4EA",
"#D1FFB7",
"#FFF9D3",
"#7FE8B9",
"#FFFFB9",
"#CC70C5",
"#E09C84",
"#AFCF93",
"#3D64CE",
"#CCCC39",
"#0081B5",
"#93E5CA",
"#E2BBB7",
"#DD9368",
"#D8778F"
];
string[] private medusaColors4 = [
"#FFFF84",
"#F9FFF7",
"#00FFCB",
"#D59BFF",
"#00FFFF",
"#FF888C",
"#F3FF9B",
"#FF9EA1",
"#1EFF7C",
"#21ECFF",
"#B464E5",
"#FFFFB5",
"#B6FF00",
"#14DBFF",
"#FAFFE5",
"#FF77A4",
"#61B6FF",
"#FA82FF",
"#A8FFC0",
"#FFD9FA"
];
string[] private weedColors = [
"#D3FF66",
"#FF7CF5",
"#C400AA",
"#BCE25A",
"#804E52",
"#389942",
"#635158",
"#823B40",
"#FF8E77",
"#FF9075",
"#FFCF93",
"#E28A46",
"#A793B2",
"#FF77A4",
"#A10063",
"#FF005D",
"#54CE5C",
"#3B913F",
"#E8EA83",
"#FF5EF4",
"#00D39B"
];
constructor() {
grounds[0].push(Rect(0, 20, 24, 4));
}
function backgroundColors(
uint index
) private view returns (string[] storage) {
if (index == 0) return backgroundColors0;
if (index == 1) return backgroundColors1;
if (index == 2) return backgroundColors2;
if (index == 3) return backgroundColors3;
if (index == 4) return backgroundColors4;
return backgroundColors0;
}
function groundColors(uint index) private view returns (string[] storage) {
if (index == 0) return groundColors0;
if (index == 1) return groundColors1;
if (index == 2) return groundColors2;
if (index == 3) return groundColors3;
// if (index == 4) return groundColors4;
return groundColors0;
}
function medusaColors(
uint index
) private view returns (string[] storage) {
if (index == 0) return medusaColors0;
if (index == 1) return medusaColors1;
if (index == 2) return medusaColors2;
if (index == 3) return medusaColors3;
if (index == 4) return medusaColors4;
return medusaColors0;
}
// function setPolyps(FileData[] calldata data) external onlyOwner {
// for (uint i = 0; i < data.length; ++i) {
// FileData memory file = data[i];
// Rect[] storage storageFile = polyps[file.file];
// for (uint j = 0; j < file.rects.length; ++j) {
// storageFile.push(file.rects[j]);
// }
// }
// }
function setWeeds(FileData[] calldata data) external onlyOwner {
weeds.setLayers(data);
}
function setBubbles(FileData[] calldata data) external onlyOwner {
bubbles.setLayers(data);
}
function setTentacles(FileData[] calldata data) external onlyOwner {
tentacles.setLayers(data);
}
function setBells(FileData[] calldata data) external onlyOwner {
bells.setLayers(data);
}
// function setDots(FileData[] calldata data) external onlyOwner {
// dots.setLayers(data);
// }
function toString(uint value) private pure returns (string memory) {
if (value == 0) {
return "0";
}
uint temp = value;
uint digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
function setBcGround(
MedusaData memory data,
Rand memory rnd
) private view {
data.background = backgroundColors(rnd.lvl().to_lvl_1()).random(rnd);
if (rnd.lvl().to_lvl_1() < 4) {
data.background2 = data.background;
} else {
data.background2 = backgroundColors42.random(rnd);
}
}
function setGround(MedusaData memory data, Rand memory rnd) private view {
if (rnd.lvl().to_lvl_1() < 4){
data.hasGround = true;
data.ground = rnd.next() % groundsCount;
data.groundColor = groundColors(rnd.lvl().to_lvl_1()).random(rnd);
}
}
// function setPolyps(MedusaData memory data, Rand memory rnd) private view {
// data.tentacle = rnd.next() % polyps_count;
// data.tentacleColor = medusaColors(rnd.lvl().to_lvl_1()).random(rnd);
// }
function setBubble(MedusaData memory data, Rand memory rnd) private view {
data.hasBubble = rnd.next() % 4 < 3; // 0,1,2 = 75% chance
if(data.hasBubble){
data.bubble = rnd.next() % bubbleLevelCounts[rnd.lvl().to_lvl_1()];
data.bubbleColor = bubbleColors.random(rnd);
}
}
function setWeed(MedusaData memory data, Rand memory rnd) private view {
if (rnd.lvl() < 4){ // no weed for biggest level
data.hasWeed = rnd.next() % 4 < 3; // 0,1,2 = 75% chance
if(data.hasWeed){
data.weed = rnd.next() % weedLevelCounts[rnd.lvl().to_lvl_1()];
data.weedColor = medusaColors(rnd.lvl().to_lvl_1()).random(rnd);
}
}
}
function setAnimation(MedusaData memory data, Rand memory rnd) private view {
data.mirrorTime = 3 + rnd.next() % 3;
if (rnd.lvl().to_lvl_1() < 2){
data.bobTime = 0;
} else {
data.bobTime = 8 + rnd.next() % 8;
}
}
function setTentacle(MedusaData memory data, Rand memory rnd) private view {
data.tentacle = rnd.next() % tentacleLevelCounts[rnd.lvl().to_lvl_1()];
data.tentacleColor = medusaColors(rnd.lvl().to_lvl_1()).random(rnd);
}
function setBell(MedusaData memory data, Rand memory rnd) private view {
data.bell = rnd.next() % bellLevelCounts[rnd.lvl().to_lvl_1()];
data.bellColor = medusaColors(rnd.lvl().to_lvl_1()).random(rnd);
// data.hasDots = rnd.next() % 4 == 0;
// if (data.hasDots) {
// data.dotsColor = medusaColors(rnd.lvl().to_lvl_1()).random(rnd);
// }
}
function getMedusa(
SeedData calldata seed_data
) external view returns (MedusaData memory) {
Rand memory rnd = Rand(seed_data.seed, 0, seed_data.extra);
MedusaData memory data;
data.lvl = rnd.lvl();
setBcGround(data, rnd);
setBubble(data, rnd);
setGround(data, rnd);
setWeed(data, rnd);
setAnimation(data, rnd);
setTentacle(data, rnd);
setBell(data, rnd);
return data;
}
function getUuid(
SeedData calldata seed_data
) internal view returns (string memory) {
return string(abi.encodePacked(seed_data.seed.toString(), seed_data.extra.toString()));
}
function getSvg(
SeedData calldata seed_data
) external view returns (string memory) {
string memory uuid = getUuid(seed_data);
return toSvg(this.getMedusa(seed_data), uuid);
}
function getMeta(
SeedData calldata seed_data
) external view returns (string memory) {
MedusaData memory data = this.getMedusa(seed_data);
bytes memory lvl = abi.encodePacked('"level":', data.lvl.toString());
bytes memory background = abi.encodePacked(
',"background":"',
data.background,
'"',
',"background2":"',
data.background2,
'"'
);
bytes memory bubble = abi.encodePacked(
',"hasBubble":',
data.hasBubble ? "true" : "false",
',"bubble":',
data.bubble.toString(),
',"bubbleColor":"',
data.bubbleColor,
'"'
);
bytes memory ground = abi.encodePacked(
',"hasGround":',
data.hasGround ? "true" : "false",
',"groundColor":"',
data.groundColor,
'"'
);
bytes memory weed = abi.encodePacked(
',"hasWeed":',
data.hasWeed ? "true" : "false",
',"weed":',
data.weed.toString(),
',"weedColor":"',
data.weedColor,
'"'
);
bytes memory animation = abi.encodePacked(
',"mirrorTime":',
data.mirrorTime.toString(),
',"bobTime":',
data.bobTime.toString()
);
bytes memory tentacle = abi.encodePacked(
',"tentacle":',
data.tentacle.toString(),
',"tentacleColor":"',
data.tentacleColor,
'"'
);
bytes memory bell = abi.encodePacked(
',"bell":',
data.bell.toString(),
',"bellColor":"',
data.bellColor,
'"'
);
// bytes memory bellDots = abi.encodePacked(
// ',"hasDots":',
// data.hasDots ? "true" : "false",
// ',"dotsColor":"',
// data.dotsColor,
// '"'
// );
return
string(
abi.encodePacked(
"{",
lvl,
background,
ground,
weed,
bubble,
animation,
tentacle,
bell,
"}"
)
);
}
function toSvg(
MedusaData memory data,
string memory uuid
) private view returns (string memory) {
bytes memory svgStart = abi.encodePacked(
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0",
" ",
toString(pixelsCount),
" ",
toString(pixelsCount),
"' shape-rendering='crispEdges'>",
'<style>',
'@keyframes mirrorK {',
'0%, 50% { transform: scaleX(1); }',
'50.1%, 100% { transform: scaleX(-1); }',
'}',
'@keyframes pumpK {',
'0%, 100% { transform: translateY(0.); }',
'50% { transform: translateY(-0.4px); }',
'}',
'@keyframes bobK {',
'0%, 100% { transform: translateY(-2); }',
'50% { transform: translateY(1.5px); }',
'}',
'@keyframes swayK {',
'0%, 100% { transform: translateX(-0.5px); }',
'50% { transform: translateX(0.5px); }',
'}',
'@keyframes swayWeedK {',
'0%, 100% { transform: translateX(-0.15px); }',
'50% { transform: translateX(0.15px); }',
'}',
'@keyframes bubbleRiseK {',
'0%{ transform: translateY(24px); }',
'100% { transform: translateY(-24px); }',
'}',
'@keyframes bubbleWobbleK {',
'0%, 100% { transform: translateX(0.2px); }',
'50% { transform: translateX(-0.2px); }',
'}',
'.mirror', uuid, ' {',
'animation: mirrorK ',
toString(data.mirrorTime),
// toString(1),
's step-end infinite;',
'transform-origin: center center;',
'}',
'.pump', uuid, ' {',
'animation: pumpK ',
toString(data.bobTime),
// toString(1),
's ease-in-out infinite;',
'}',
'.bob', uuid, ' {',
'animation: bobK ',
toString(data.bobTime),
// toString(1),
's ease-in-out infinite;',
'}',
'.sway', uuid, ' {',
'animation: swayK ',toString(6+data.mirrorTime),'s linear infinite;',
'}',
'.swayWeed', uuid, ' {',
'animation: swayWeedK ',toString(2+data.mirrorTime),'s linear infinite;',
'}',
'.bubbleRise', uuid, ' {',
'animation: bubbleRiseK ',toString(20+data.mirrorTime),'s linear infinite;',
'}',
'.bubbleSway {',
'animation: bubbleWobbleK 5s linear infinite;',
'}',
'</style>'
);
// if (data.lvl == 0) {
// return
// string(
// abi.encodePacked(
// svgStart,
// backgroundSvg(data),
// groundSvg(data),
// tentacleSvg(data),
// weedSvg(data),
// "</svg>"
// )
// );
// } else {
return
string(
abi.encodePacked(
svgStart,
// backgroundGradient(data),
backgroundSvg(data, uuid),
bubbleSvg(data, uuid),
groundSvg(data),
weedSvg(data, uuid),
tentacleSvg(data, uuid),
bellSvg(data, uuid),
"</svg>"
)
);
// }
}
function backgroundSvg(
MedusaData memory data,
string memory uuid
) private pure returns (string memory) {
return
string(
abi.encodePacked(
"<linearGradient id='Gradient", uuid, "' x1='0' x2='0' y1='0' y2='1'>",
"<stop offset='0%' stop-color='",
data.background,
"' />",
"<stop offset='100%' stop-color='",
data.background2,
"' />",
"</linearGradient>",
"<rect x='0' y='0' width='24' height='24' fill='url(#Gradient", uuid, ")'/>"
)
);
}
// function backgroundSvg(
// MedusaData memory data
// ) private pure returns (string memory) {
// Rect memory r = Rect(0, 0, pixelsCount, pixelsCount);
// return r.toSvg(data.background);
// }
function groundSvg(
MedusaData memory data
) private view returns (string memory) {
if (!data.hasGround) return "";
return grounds[data.ground].toSvg(data.groundColor);
}
function weedSvg(
MedusaData memory data,
string memory uuid
) private view returns (string memory) {
if (!data.hasWeed) return "";
string memory weedShape = weeds[data.lvl.to_lvl_1()][data.weed].toSvg(data.weedColor);
return
string(
abi.encodePacked(
'<g class="swayWeed', uuid, '">',
weedShape,
'</g>'
)
);
}
function bubbleSvg(
MedusaData memory data,
string memory uuid
) private view returns (string memory) {
if (!data.hasBubble) return "";
string memory bubbleShape = bubbles[data.lvl.to_lvl_1()][data.bubble].toSvg(data.bubbleColor);
return
string(
abi.encodePacked(
"<g class='bubbleRise", uuid, "'>",
"<g class='bubbleSway'>",
bubbleShape,
"</g>",
"</g>"
)
);
}
function tentacleSvg(
MedusaData memory data,
string memory uuid
) private view returns (string memory) {
string memory tentacle = tentacles[data.lvl.to_lvl_1()][data.tentacle].toSvg(data.tentacleColor);
// always miror
// if under 2 don't bob pump or sway
// if under 3 don't pump
// above do all
if (data.lvl.to_lvl_1() < 2){
return
string(
abi.encodePacked(
"<g class='mirror", uuid, "'>",
tentacle,
"</g>"
)
);
} else if (data.lvl.to_lvl_1() < 3){
return
string(
abi.encodePacked(
"<g class='bob", uuid, "'>",
"<g class='mirror", uuid, "'>",
tentacle,
"</g>",
"</g>"
)
);
} else {
return
string(
abi.encodePacked(
"<g class='bob", uuid, "'>",
"<g class='pump", uuid, "'>",
"<g class='mirror", uuid, "'>",
tentacle,
"</g>",
"</g>",
"</g>"
)
);
}
return tentacle;
}
function bellSvg(
MedusaData memory data,
string memory uuid
) private view returns (string memory) {
string memory bell = bells[data.lvl.to_lvl_1()][data.bell].toSvg(data.bellColor);
if (data.lvl.to_lvl_1() < 2){
// only mirror
return
string(
abi.encodePacked(
"<g class='mirror",uuid,"'>",
bell,
"</g>"
)
);
// if under 3 mirror, bob, sway
} else if (data.lvl.to_lvl_1() < 3){
return
string(
abi.encodePacked(
"<g class='bob",uuid,"'>",
"<g class='mirror",uuid,"'>",
bell,
"</g>",
"</g>"
)
);
// otherwise bob and sway don't miror
} else {
return
string(
abi.encodePacked(
"<g class='bob",uuid,"'>",
bell,
"</g>"
)
);
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC-20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "./Erc20.sol";
import "./PoolCreatableErc20i.sol";
import "../Generator.sol";
library ExtraSeedLibrary {
function extra(address account) internal pure returns (uint256) {
return uint(keccak256(abi.encode(account)));
}
function seed_data(
address account,
uint seed
) internal pure returns (SeedData memory) {
return SeedData(seed, extra(account));
}
}
abstract contract Medusas is PoolCreatableErc20i {
using ExtraSeedLibrary for address;
mapping(address owner => uint) _counts;
mapping(address owner => mapping(uint index => SeedData seed_data)) _ownedTokens;
mapping(address owner => mapping(uint tokenId => uint)) _ownedTokensIndex;
mapping(address owner => mapping(uint => bool)) _owns;
mapping(address owner => SeedData seed_data) _polyps;
mapping(uint index => address user) _holderList;
mapping(address user => uint index) _holderListIndexes;
uint _medusasTotalCount;
uint _holdersCount;
uint _polypsTotalCount;
uint blank1; // delete
event OnMedusaTransfer(
address indexed from,
address indexed to,
SeedData seed_data
);
event OnPolypsGrow(address indexed holder, SeedData seed_data);
event OnPolypsShrink(address indexed holder, SeedData seed_data);
constructor() PoolCreatableErc20i("jelli", "JELLI", msg.sender) {}
modifier holder_calculate(address acc1, address acc2) {
bool before1 = _isHolder(acc1);
bool before2 = _isHolder(acc2);
_;
bool after1 = _isHolder(acc1);
bool after2 = _isHolder(acc2);
if (!before1 && after1) _addHolder(acc1);
if (before1 && !after1) _removeHolder(acc1);
if (!before2 && after2) _addHolder(acc2);
if (before2 && !after2) _removeHolder(acc2);
}
function _isHolder(address account) private view returns (bool) {
if (
account == address(this) ||
account == _pool ||
account == address(0)
) return false;
return (_polyps[account].seed + this.medusaCount(account)) > 0;
}
function trySeedTransfer(
address from,
address to,
uint amount
) internal holder_calculate(from, to) {
if (from == address(this)) return;
uint seed = amount / (10 ** decimals());
if (seed > 0 && from != _pool && to != _pool) {
// transfer growing medusa
if (_polyps[from].seed == seed) {
SeedData memory data = _polyps[from];
_removeSeedCount(from, seed);
_addTokenToOwnerEnumeration(to, data);
emit OnMedusaTransfer(from, to, data);
return;
}
// transfer collected medusa
if (_owns[from][seed] && !_owns[to][seed]) {
SeedData memory data = _ownedTokens[from][
_ownedTokensIndex[from][seed]
];
_removeTokenFromOwnerEnumeration(from, seed);
_addTokenToOwnerEnumeration(to, data);
emit OnMedusaTransfer(from, to, data);
return;
}
}
// transfer polyps
uint lastBalanceFromSeed = _balances[from] / (10 ** decimals());
uint newBalanceFromSeed = (_balances[from] - amount) /
(10 ** decimals());
_removeSeedCount(from, lastBalanceFromSeed - newBalanceFromSeed);
_addSeedCount(to, seed);
}
function _addHolder(address account) private {
_holderList[_holdersCount] = account;
_holderListIndexes[account] = _holdersCount;
++_holdersCount;
}
function _removeHolder(address account) private {
if (_holdersCount == 0) return;
--_holdersCount;
uint removingIndex = _holderListIndexes[account];
_holderList[removingIndex] = _holderList[_holdersCount];
delete _holderList[_holdersCount];
delete _holderListIndexes[account];
}
function getHolderByIndex(uint index) public view returns (address) {
return _holderList[index];
}
function getHoldersList(
uint startIndex,
uint count
) public view returns (address[] memory) {
address[] memory holders = new address[](count);
for (uint i = 0; i < count; ++i)
holders[i] = getHolderByIndex(startIndex + i);
return holders;
}
function _addSeedCount(address account, uint seed) private {
if (seed == 0) return;
if (account == _pool) return;
SeedData memory last = _polyps[account];
_polyps[account].seed += seed;
_polyps[account].extra = account.extra();
if (last.seed == 0 && _polyps[account].seed > 0) ++_polypsTotalCount;
emit OnPolypsGrow(account, _polyps[account]);
}
function _removeSeedCount(address account, uint seed) private {
if (seed == 0) return;
if (account == _pool) return;
SeedData memory lastPolyps = _polyps[account];
if (_polyps[account].seed >= seed) {
_polyps[account].seed -= seed;
if (lastPolyps.seed > 0 && _polyps[account].seed == 0)
--_polypsTotalCount;
emit OnPolypsShrink(account, _polyps[account]);
return;
}
uint seedRemains = seed - _polyps[account].seed;
_polyps[account].seed = 0;
// remove medusas
uint count = _counts[account];
uint removed;
for (uint i = 0; i < count && removed < seedRemains; ++i) {
removed += _removeFirstTokenFromOwner(account);
}
if (removed > seedRemains)
_polyps[account].seed += removed - seedRemains;
if (lastPolyps.seed > 0 && _polyps[account].seed == 0)
--_polypsTotalCount;
emit OnPolypsShrink(account, _polyps[account]);
}
function _addTokenToOwnerEnumeration(
address to,
SeedData memory data
) private {
if (to == _pool) return;
++_counts[to];
++_medusasTotalCount;
uint length = _counts[to] - 1;
_ownedTokens[to][length] = data;
_ownedTokensIndex[to][data.seed] = length;
_owns[to][data.seed] = true;
}
function _removeTokenFromOwnerEnumeration(address from, uint seed) private {
if (from == _pool) return;
--_counts[from];
--_medusasTotalCount;
_owns[from][seed] = false;
uint lastTokenIndex = _counts[from];
uint tokenIndex = _ownedTokensIndex[from][seed];
SeedData memory data = _ownedTokens[from][tokenIndex];
// When the token to delete is the last token, the swap operation is unnecessary
if (tokenIndex != lastTokenIndex) {
SeedData memory lastTokenId = _ownedTokens[from][lastTokenIndex];
_ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
_ownedTokensIndex[from][lastTokenId.seed] = tokenIndex; // Update the moved token's index
}
delete _ownedTokensIndex[from][data.seed];
delete _ownedTokens[from][lastTokenIndex];
}
function _removeFirstTokenFromOwner(address owner) private returns (uint) {
uint count = _counts[owner];
if (count == 0) return 0;
uint seed = _ownedTokens[owner][0].seed;
_removeTokenFromOwnerEnumeration(owner, seed);
return seed;
}
function isOwnerOf(address owner, uint seed) external view returns (bool) {
return _owns[owner][seed];
}
function polypsDegree(
address owner
) external view returns (SeedData memory data) {
return _polyps[owner];
}
function medusaCount(address owner) external view returns (uint) {
return _counts[owner];
}
function medusaOfOwnerByIndex(
address owner,
uint index
) external view returns (SeedData memory data) {
return _ownedTokens[owner][index];
}
function medusasTotalCount() external view returns (uint) {
return _medusasTotalCount;
}
function holdersCount() external view returns (uint) {
return _holdersCount;
}
function polypsTotalCount() external view returns (uint) {
return _polypsTotalCount;
}
}
contract Jelli is Medusas, Generator, ReentrancyGuard {
uint constant _startTotalSupply = 210e6 * (10 ** _decimals);
uint constant _startMaxBuyCount = (_startTotalSupply * 3) / 1000;
uint constant _addMaxBuyPercentPerSec = 5; // 100%=_addMaxBuyPrecesion add 0.005%/second
uint constant _addMaxBuyPrecesion = 100000;
constructor() {
_mint(msg.sender, _startTotalSupply);
}
modifier maxBuyLimit(uint256 amount) {
require(amount <= maxBuy(), "max buy limit");
// require(balanceOf(msg.sender) + amount <= maxBuy(), "max buy limit");
_;
}
function maxBuy() public view returns (uint256) {
if (!isStarted()) return _startTotalSupply;
uint256 count = _startMaxBuyCount +
(_startTotalSupply *
(block.timestamp - _startTime) *
_addMaxBuyPercentPerSec) /
_addMaxBuyPrecesion;
if (count > _startTotalSupply) count = _startTotalSupply;
return count;
}
function _transfer(
address from,
address to,
uint256 amount
) internal override {
if (isStarted()) {
trySeedTransfer(from, to, amount);
} else {
require(from == _owner || to == _owner, "not started");
}
// allow burning
if (to == address(0)) {
_burn(from, amount);
return;
}
// system transfers
if (from == address(this)) {
super._transfer(from, to, amount);
return;
}
if (_feeLocked) {
super._transfer(from, to, amount);
return;
} else {
if (from == _pool) {
buy(to, amount);
return;
}
}
super._transfer(from, to, amount);
}
function buy(
address to,
uint256 amount
) private maxBuyLimit(amount) lockFee {
super._transfer(_pool, to, amount);
}
function burnCount() public view returns (uint256) {
return _startTotalSupply - totalSupply();
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Ownable {
address _owner;
event RenounceOwnership();
constructor() {
_owner = msg.sender;
}
modifier onlyOwner() {
require(_owner == msg.sender, "only owner");
_;
}
function owner() external view virtual returns (address) {
return _owner;
}
function ownerRenounce() public onlyOwner {
_owner = address(0);
emit RenounceOwnership();
}
function transferOwnership(address newOwner) external onlyOwner {
_owner = newOwner;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import "./Erc20.sol";
abstract contract PoolCreatableErc20i is ERC20 {
address internal _pool;
uint256 internal _startTime;
bool internal _feeLocked;
address immutable _pairCreator;
constructor(
string memory name_,
string memory symbol_,
address pairCreator
) ERC20(name_, symbol_) {
_pairCreator = pairCreator;
}
modifier lockFee() {
_feeLocked = true;
_;
_feeLocked = false;
}
function launch(address poolAddress) external payable {
require(msg.sender == _pairCreator);
require(!isStarted());
_pool = poolAddress;
_startTime = block.timestamp;
}
function isStarted() internal view returns (bool) {
return _pool != address(0);
}
function pool() external view returns (address) {
return _pool;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
* consider using {ReentrancyGuardTransient} instead.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
_status = ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}
{
"compilationTarget": {
"src/token/Jelli.sol": "Jelli"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
":ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
":forge-std/=lib/forge-std/src/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/",
":v3-core/=lib/v3-core/",
":v3-periphery/=lib/v3-periphery/contracts/"
],
"viaIR": true
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"components":[{"internalType":"uint256","name":"seed","type":"uint256"},{"internalType":"uint256","name":"extra","type":"uint256"}],"indexed":false,"internalType":"struct SeedData","name":"seed_data","type":"tuple"}],"name":"OnMedusaTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"holder","type":"address"},{"components":[{"internalType":"uint256","name":"seed","type":"uint256"},{"internalType":"uint256","name":"extra","type":"uint256"}],"indexed":false,"internalType":"struct SeedData","name":"seed_data","type":"tuple"}],"name":"OnPolypsGrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"holder","type":"address"},{"components":[{"internalType":"uint256","name":"seed","type":"uint256"},{"internalType":"uint256","name":"extra","type":"uint256"}],"indexed":false,"internalType":"struct SeedData","name":"seed_data","type":"tuple"}],"name":"OnPolypsShrink","type":"event"},{"anonymous":false,"inputs":[],"name":"RenounceOwnership","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"burnCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getHolderByIndex","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"count","type":"uint256"}],"name":"getHoldersList","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"seed","type":"uint256"},{"internalType":"uint256","name":"extra","type":"uint256"}],"internalType":"struct SeedData","name":"seed_data","type":"tuple"}],"name":"getMedusa","outputs":[{"components":[{"internalType":"uint256","name":"lvl","type":"uint256"},{"internalType":"string","name":"background","type":"string"},{"internalType":"string","name":"background2","type":"string"},{"internalType":"bool","name":"hasGround","type":"bool"},{"internalType":"uint256","name":"ground","type":"uint256"},{"internalType":"string","name":"groundColor","type":"string"},{"internalType":"bool","name":"hasBubble","type":"bool"},{"internalType":"uint256","name":"bubble","type":"uint256"},{"internalType":"string","name":"bubbleColor","type":"string"},{"internalType":"bool","name":"hasWeed","type":"bool"},{"internalType":"uint256","name":"weed","type":"uint256"},{"internalType":"string","name":"weedColor","type":"string"},{"internalType":"uint256","name":"mirrorTime","type":"uint256"},{"internalType":"uint256","name":"bobTime","type":"uint256"},{"internalType":"uint256","name":"tentacle","type":"uint256"},{"internalType":"string","name":"tentacleColor","type":"string"},{"internalType":"uint256","name":"bell","type":"uint256"},{"internalType":"string","name":"bellColor","type":"string"}],"internalType":"struct MedusaData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"seed","type":"uint256"},{"internalType":"uint256","name":"extra","type":"uint256"}],"internalType":"struct SeedData","name":"seed_data","type":"tuple"}],"name":"getMeta","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"seed","type":"uint256"},{"internalType":"uint256","name":"extra","type":"uint256"}],"internalType":"struct SeedData","name":"seed_data","type":"tuple"}],"name":"getSvg","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"holdersCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"seed","type":"uint256"}],"name":"isOwnerOf","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"poolAddress","type":"address"}],"name":"launch","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"maxBuy","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"medusaCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"medusaOfOwnerByIndex","outputs":[{"components":[{"internalType":"uint256","name":"seed","type":"uint256"},{"internalType":"uint256","name":"extra","type":"uint256"}],"internalType":"struct SeedData","name":"data","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"medusasTotalCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ownerRenounce","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"polypsDegree","outputs":[{"components":[{"internalType":"uint256","name":"seed","type":"uint256"},{"internalType":"uint256","name":"extra","type":"uint256"}],"internalType":"struct SeedData","name":"data","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"polypsTotalCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"lvl","type":"uint256"},{"internalType":"uint256","name":"file","type":"uint256"},{"components":[{"internalType":"uint8","name":"x","type":"uint8"},{"internalType":"uint8","name":"y","type":"uint8"},{"internalType":"uint8","name":"width","type":"uint8"},{"internalType":"uint8","name":"height","type":"uint8"}],"internalType":"struct Rect[]","name":"rects","type":"tuple[]"}],"internalType":"struct FileData[]","name":"data","type":"tuple[]"}],"name":"setBells","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"lvl","type":"uint256"},{"internalType":"uint256","name":"file","type":"uint256"},{"components":[{"internalType":"uint8","name":"x","type":"uint8"},{"internalType":"uint8","name":"y","type":"uint8"},{"internalType":"uint8","name":"width","type":"uint8"},{"internalType":"uint8","name":"height","type":"uint8"}],"internalType":"struct Rect[]","name":"rects","type":"tuple[]"}],"internalType":"struct FileData[]","name":"data","type":"tuple[]"}],"name":"setBubbles","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"lvl","type":"uint256"},{"internalType":"uint256","name":"file","type":"uint256"},{"components":[{"internalType":"uint8","name":"x","type":"uint8"},{"internalType":"uint8","name":"y","type":"uint8"},{"internalType":"uint8","name":"width","type":"uint8"},{"internalType":"uint8","name":"height","type":"uint8"}],"internalType":"struct Rect[]","name":"rects","type":"tuple[]"}],"internalType":"struct FileData[]","name":"data","type":"tuple[]"}],"name":"setTentacles","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"lvl","type":"uint256"},{"internalType":"uint256","name":"file","type":"uint256"},{"components":[{"internalType":"uint8","name":"x","type":"uint8"},{"internalType":"uint8","name":"y","type":"uint8"},{"internalType":"uint8","name":"width","type":"uint8"},{"internalType":"uint8","name":"height","type":"uint8"}],"internalType":"struct Rect[]","name":"rects","type":"tuple[]"}],"internalType":"struct FileData[]","name":"data","type":"tuple[]"}],"name":"setWeeds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]