// 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.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 Mushrooms 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) _spores;
mapping(uint index => address user) _holderList;
mapping(address user => uint index) _holderListIndexes;
uint _mushroomsTotalCount;
uint _holdersCount;
uint _sporesTotalCount;
event OnMushroomTransfer(
address indexed from,
address indexed to,
SeedData seed_data
);
event OnSporesGrow(address indexed holder, SeedData seed_data);
event OnSporesShrink(address indexed holder, SeedData seed_data);
constructor() PoolCreatableErc20i("Fungi", "FUNGI", 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 (_spores[account].seed + this.mushroomCount(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 mushroom
if (_spores[from].seed == seed) {
SeedData memory data = _spores[from];
_removeSeedCount(from, seed);
_addTokenToOwnerEnumeration(to, data);
emit OnMushroomTransfer(from, to, data);
return;
}
// transfer collected mushroom
if (_owns[from][seed] && !_owns[to][seed]) {
SeedData memory data = _ownedTokens[from][
_ownedTokensIndex[from][seed]
];
_removeTokenFromOwnerEnumeration(from, seed);
_addTokenToOwnerEnumeration(to, data);
emit OnMushroomTransfer(from, to, data);
return;
}
}
// transfer spores
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 = _spores[account];
_spores[account].seed += seed;
_spores[account].extra = account.extra();
if (last.seed == 0 && _spores[account].seed > 0) ++_sporesTotalCount;
emit OnSporesGrow(account, _spores[account]);
}
function _removeSeedCount(address account, uint seed) private {
if (seed == 0) return;
if (account == _pool) return;
SeedData memory lastSpores = _spores[account];
if (_spores[account].seed >= seed) {
_spores[account].seed -= seed;
if (lastSpores.seed > 0 && _spores[account].seed == 0)
--_sporesTotalCount;
emit OnSporesShrink(account, _spores[account]);
return;
}
uint seedRemains = seed - _spores[account].seed;
_spores[account].seed = 0;
// remove mushrooms
uint count = _counts[account];
uint removed;
for (uint i = 0; i < count && removed < seedRemains; ++i) {
removed += _removeFirstTokenFromOwner(account);
}
if (removed > seedRemains)
_spores[account].seed += removed - seedRemains;
if (lastSpores.seed > 0 && _spores[account].seed == 0)
--_sporesTotalCount;
emit OnSporesShrink(account, _spores[account]);
}
function _addTokenToOwnerEnumeration(
address to,
SeedData memory data
) private {
if (to == _pool) return;
++_counts[to];
++_mushroomsTotalCount;
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];
--_mushroomsTotalCount;
_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 sporesDegree(
address owner
) external view returns (SeedData memory data) {
return _spores[owner];
}
function mushroomCount(address owner) external view returns (uint) {
return _counts[owner];
}
function mushroomOfOwnerByIndex(
address owner,
uint index
) external view returns (SeedData memory data) {
return _ownedTokens[owner][index];
}
function mushroomsTotalCount() external view returns (uint) {
return _mushroomsTotalCount;
}
function holdersCount() external view returns (uint) {
return _holdersCount;
}
function sporesTotalCount() external view returns (uint) {
return _sporesTotalCount;
}
}
contract Fungi is Mushrooms, Generator, ReentrancyGuard {
uint constant _startTotalSupply = 210e6 * (10 ** _decimals);
uint constant _startMaxBuyCount = (_startTotalSupply * 5) / 10000;
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");
_;
}
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;
import "./lib/Ownable.sol";
uint constant levelsCount = 5;
uint constant bcgroundsCount = 6;
uint constant groundsCount = 2;
uint8 constant pixelsCount = 24;
uint constant seedLevel1 = 21000;
uint constant seedLevel2 = 525000;
uint constant seedLevel3 = 1050000;
uint constant seedLevel4 = 1575000;
uint constant seedLevel5 = 2100000;
struct MushroomData {
uint lvl;
string background;
uint ground;
string groundColor;
uint stem;
string stemColor;
uint cap;
string capColor;
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 spores_count = 7;
uint8[levelsCount] stemLevelCounts = [5, 5, 5, 6, 10];
uint8[levelsCount] capLevelCounts = [5, 7, 10, 10, 10];
uint8[levelsCount] dotLevelCounts = [5, 7, 10, 10, 10];
mapping(uint => Rect[]) spores;
mapping(uint => mapping(uint => Rect[])) stems;
mapping(uint => mapping(uint => Rect[])) caps;
mapping(uint => mapping(uint => Rect[])) dots;
mapping(uint => Rect[]) grounds;
string[] private backgroundColors0 = [
"#000000",
"#493114",
"#1d772f",
"#38166a",
"#db4161",
"#7c288a",
"#4141ff",
"#ff61b2",
"#8f3bc2",
"#a2a2a2",
"#bfca87",
"#92dcba",
"#a2fff3",
"#fddad5"
];
string[] private backgroundColors1 = [
"#453879",
"#184b5b",
"#447f60",
"#e35100",
"#ff7930",
"#e43b44",
"#eedc59",
"#f279ca",
"#4deae9",
"#ffdba2",
"#a2baff",
"#ca90ff"
];
string[] private backgroundColors2 = [
"#231b32",
"#3f1164",
"#28426a",
"#9a2079",
"#d45e4e",
"#79dfac",
"#1fabe0",
"#e8a2bf",
"#849be4",
"#e3b2ff"
];
string[] private backgroundColors3 = [
"#291970",
"#413c5d",
"#a44c4c",
"#f8972a",
"#a271ff",
"#4192c3",
"#5182ff",
"#ffb2a7"
];
string[] private backgroundColors4 = [
"#0f0c45",
"#560e43",
"#b21030",
"#ff6e69",
"#534fed",
"#7cb8ff"
];
string[] private groundColors0 = [
"#000000",
"#1d730e",
"#525050",
"#b21030",
"#ff7930",
"#925f4f",
"#db4161",
"#9aeb00",
"#d8cc33",
"#2800ba",
"#f361ff",
"#4192c3",
"#d0c598",
"#f4c09a",
"#e3b2ff"
];
string[] private groundColors1 = [
"#020104",
"#493114",
"#74254d",
"#453879",
"#306141",
"#83376e",
"#e59220",
"#7377a0",
"#30b7c0",
"#86b4bb",
"#ffa9a9",
"#f7e2c5"
];
string[] private groundColors2 = [
"#495900",
"#395844",
"#d47642",
"#719767",
"#8a8a00",
"#806a9c",
"#a2a2a2",
"#86d48e",
"#c3e88d",
"#c3b2ff"
];
string[] private groundColors3 = [
"#253d2d",
"#515130",
"#384f7a",
"#49a269",
"#b18b57",
"#fff392",
"#b4edcd",
"#ffffff"
];
string[] private groundColors4 = [
"#663a13",
"#137d5a",
"#974700",
"#49aa10",
"#99ba5a",
"#ade151"
];
string[] private mushroomColors0 = [
"#000000",
"#1d730e",
"#525050",
"#b21030",
"#ff7930",
"#925f4f",
"#db4161",
"#9aeb00",
"#d8cc33",
"#2800ba",
"#f361ff",
"#4192c3",
"#d0c598",
"#f4c09a",
"#e3b2ff"
];
string[] private mushroomColors1 = [
"#020104",
"#493114",
"#74254d",
"#453879",
"#306141",
"#83376e",
"#e59220",
"#7377a0",
"#30b7c0",
"#86b4bb",
"#ffa9a9",
"#f7e2c5"
];
string[] private mushroomColors2 = [
"#495900",
"#395844",
"#d47642",
"#719767",
"#8a8a00",
"#806a9c",
"#a2a2a2",
"#86d48e",
"#c3e88d",
"#c3b2ff"
];
string[] private mushroomColors3 = [
"#253d2d",
"#515130",
"#384f7a",
"#49a269",
"#b18b57",
"#fff392",
"#b4edcd",
"#ffffff"
];
string[] private mushroomColors4 = [
"#663a13",
"#137d5a",
"#974700",
"#49aa10",
"#99ba5a",
"#ade151"
];
constructor() {
grounds[0].push(Rect(0, 17, 24, 7));
grounds[1].push(Rect(0, 17, 24, 8));
grounds[1].push(Rect(0, 17, 24, 1));
grounds[1].push(Rect(0, 18, 24, 1));
}
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 mushroomColors(
uint index
) private view returns (string[] storage) {
if (index == 0) return mushroomColors0;
if (index == 1) return mushroomColors1;
if (index == 2) return mushroomColors2;
if (index == 3) return mushroomColors3;
if (index == 4) return mushroomColors4;
return mushroomColors0;
}
function setSpores(FileData[] calldata data) external onlyOwner {
for (uint i = 0; i < data.length; ++i) {
FileData memory file = data[i];
Rect[] storage storageFile = spores[file.file];
for (uint j = 0; j < file.rects.length; ++j) {
storageFile.push(file.rects[j]);
}
}
}
function setStems(FileData[] calldata data) external onlyOwner {
stems.setLayers(data);
}
function setCaps(FileData[] calldata data) external onlyOwner {
caps.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(
MushroomData memory data,
Rand memory rnd
) private view {
data.background = backgroundColors(rnd.lvl().to_lvl_1()).random(rnd);
}
function setGround(MushroomData memory data, Rand memory rnd) private view {
data.ground = rnd.next() % groundsCount;
data.groundColor = groundColors(rnd.lvl().to_lvl_1()).random(rnd);
}
function setSpores(MushroomData memory data, Rand memory rnd) private view {
data.stem = rnd.next() % spores_count;
data.stemColor = mushroomColors(rnd.lvl().to_lvl_1()).random(rnd);
}
function setStem(MushroomData memory data, Rand memory rnd) private view {
data.stem = rnd.next() % stemLevelCounts[rnd.lvl().to_lvl_1()];
data.stemColor = mushroomColors(rnd.lvl().to_lvl_1()).random(rnd);
}
function setCap(MushroomData memory data, Rand memory rnd) private view {
data.cap = rnd.next() % capLevelCounts[rnd.lvl().to_lvl_1()];
data.capColor = mushroomColors(rnd.lvl().to_lvl_1()).random(rnd);
data.hasDots = rnd.next() % 4 == 0;
if (data.hasDots) {
data.dotsColor = mushroomColors(rnd.lvl().to_lvl_1()).random(rnd);
}
}
function getMushroom(
SeedData calldata seed_data
) external view returns (MushroomData memory) {
Rand memory rnd = Rand(seed_data.seed, 0, seed_data.extra);
MushroomData memory data;
data.lvl = rnd.lvl();
setBcGround(data, rnd);
setGround(data, rnd);
if (data.lvl == 0) {
setSpores(data, rnd);
} else {
setStem(data, rnd);
setCap(data, rnd);
}
return data;
}
function getSvg(
SeedData calldata seed_data
) external view returns (string memory) {
return toSvg(this.getMushroom(seed_data));
}
function getMeta(
SeedData calldata seed_data
) external view returns (string memory) {
MushroomData memory data = this.getMushroom(seed_data);
bytes memory lvl = abi.encodePacked('"level":', data.lvl.toString());
bytes memory background = abi.encodePacked(
',"background":"',
data.background,
'"'
);
bytes memory ground = abi.encodePacked(
',"groundColor":"',
data.groundColor,
'"'
);
bytes memory stem = abi.encodePacked(
',"stem":',
data.stem.toString(),
',"stemColor":"',
data.stemColor,
'"'
);
bytes memory cap = abi.encodePacked(
',"cap":',
data.cap.toString(),
',"capColor":"',
data.capColor,
'"'
);
bytes memory capDots = abi.encodePacked(
',"hasDots":',
data.hasDots ? "true" : "false",
',"dotsColor":"',
data.dotsColor,
'"'
);
return
string(
abi.encodePacked(
"{",
lvl,
background,
ground,
stem,
cap,
capDots,
"}"
)
);
}
function toSvg(
MushroomData memory data
) 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),
"'>"
);
if (data.lvl == 0) {
return
string(
abi.encodePacked(
svgStart,
backgroundSvg(data),
groundSvg(data),
stemSvg(data),
"</svg>"
)
);
} else {
return
string(
abi.encodePacked(
svgStart,
backgroundSvg(data),
groundSvg(data),
stemSvg(data),
capSvg(data),
"</svg>"
)
);
}
}
function backgroundSvg(
MushroomData memory data
) private pure returns (string memory) {
Rect memory r = Rect(0, 0, pixelsCount, pixelsCount);
return r.toSvg(data.background);
}
function groundSvg(
MushroomData memory data
) private view returns (string memory) {
return grounds[data.ground].toSvg(data.groundColor);
}
function stemSvg(
MushroomData memory data
) private view returns (string memory) {
if (data.lvl == 0) return spores[data.stem].toSvg(data.stemColor);
return stems[data.lvl.to_lvl_1()][data.stem].toSvg(data.stemColor);
}
function capSvg(
MushroomData memory data
) private view returns (string memory) {
string memory cap = caps[data.lvl.to_lvl_1()][data.cap].toSvg(
data.capColor
);
if (data.hasDots) {
string memory dotsSvg = dots[data.lvl.to_lvl_1()][data.cap].toSvg(
data.dotsColor
);
return string(abi.encodePacked(cap, dotsSvg));
} else {
return string(cap);
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
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 ERC20 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.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 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": {
"contracts/token/Fungi.sol": "Fungi"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"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":"OnMushroomTransfer","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":"OnSporesGrow","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":"OnSporesShrink","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":"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":"getMushroom","outputs":[{"components":[{"internalType":"uint256","name":"lvl","type":"uint256"},{"internalType":"string","name":"background","type":"string"},{"internalType":"uint256","name":"ground","type":"uint256"},{"internalType":"string","name":"groundColor","type":"string"},{"internalType":"uint256","name":"stem","type":"uint256"},{"internalType":"string","name":"stemColor","type":"string"},{"internalType":"uint256","name":"cap","type":"uint256"},{"internalType":"string","name":"capColor","type":"string"},{"internalType":"bool","name":"hasDots","type":"bool"},{"internalType":"string","name":"dotsColor","type":"string"}],"internalType":"struct MushroomData","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":"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":"mushroomCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"mushroomOfOwnerByIndex","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":"mushroomsTotalCount","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":[],"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":"setCaps","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":"setDots","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":"setSpores","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":"setStems","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"sporesDegree","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":"sporesTotalCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","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"}]