// 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";
uint constant _TotalSupplyParam = 100e6;
library ExtraSeedLibrary {
function extra(
address account,
uint extraSeed
) internal pure returns (uint256) {
return uint(keccak256(abi.encodePacked(account, extraSeed)));
}
function seed_data(
address account,
uint seed,
uint extraSeed
) internal pure returns (SeedData memory) {
return SeedData(seed, extra(account, extraSeed));
}
}
abstract contract Inscriptions 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) _dynamicInscription;
mapping(uint index => address user) _holderList;
mapping(address user => uint index) _holderListIndexes;
uint _inscriptionsTotalCount;
uint _holdersCount;
uint _dynamicInscriptionTotalCount;
uint _random_nonce;
event OnInscriptionTransfer(
address indexed from,
address indexed to,
SeedData seed_data
);
event OnDynamicInscriptionAdd(address indexed holder, SeedData seed_data);
event OnDynamicInscriptionRemove(address indexed holder, SeedData seed_data);
constructor() PoolCreatableErc20i("Froggi", "$froggi", msg.sender) {
_addHolder(msg.sender);
SeedData memory seed = SeedData(_TotalSupplyParam, 1);
_addSeedCount(msg.sender, seed.seed);
}
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
(_dynamicInscription[account].seed +
this.inscriptionCount(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 inscription
if (_dynamicInscription[from].seed == seed && !_owns[to][seed]) {
SeedData memory data = _dynamicInscription[from];
_removeSeedCount(from, seed);
_addTokenToOwnerEnumeration(to, data);
emit OnInscriptionTransfer(from, to, data);
return;
}
// transfer collected inscription
if (_owns[from][seed] && !_owns[to][seed]) {
SeedData memory data = _ownedTokens[from][
_ownedTokensIndex[from][seed]
];
_removeTokenFromOwnerEnumeration(from, seed);
_addTokenToOwnerEnumeration(to, data);
emit OnInscriptionTransfer(from, to, data);
return;
}
}
// transfer dynamicInscription
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;
uint removingIndex = _holderListIndexes[account];
if (removingIndex != _holdersCount - 1) {
address lastHolder = _holderList[_holdersCount - 1];
_holderList[removingIndex] = lastHolder;
_holderListIndexes[lastHolder] = removingIndex;
}
--_holdersCount;
delete _holderListIndexes[account];
delete _holderList[_holdersCount];
}
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 = _dynamicInscription[account];
_dynamicInscription[account].seed += seed;
_dynamicInscription[account].extra = account.extra(++_random_nonce);
if (last.seed == 0 && _dynamicInscription[account].seed > 0)
++_dynamicInscriptionTotalCount;
emit OnDynamicInscriptionAdd(account, _dynamicInscription[account]);
}
function _removeSeedCount(address account, uint seed) private {
if (seed == 0) return;
if (account == _pool) return;
SeedData memory lastDynamicInscription = _dynamicInscription[account];
if (_dynamicInscription[account].seed >= seed) {
_dynamicInscription[account].seed -= seed;
_dynamicInscription[account].extra = account.extra(++_random_nonce);
if (lastDynamicInscription.seed > 0 && _dynamicInscription[account].seed == 0)
--_dynamicInscriptionTotalCount;
emit OnDynamicInscriptionRemove(account, _dynamicInscription[account]);
return;
}
uint seedRemains = seed - _dynamicInscription[account].seed;
_dynamicInscription[account].seed = 0;
_dynamicInscription[account].extra = account.extra(++_random_nonce);
// remove inscriptions
uint count = _counts[account];
uint removed;
for (uint i = 0; i < count && removed < seedRemains; ++i) {
removed += _removeFirstTokenFromOwner(account);
}
if (removed > seedRemains) {
_dynamicInscription[account].seed += removed - seedRemains;
_dynamicInscription[account].extra = account.extra(++_random_nonce);
}
if (lastDynamicInscription.seed > 0 && _dynamicInscription[account].seed == 0)
--_dynamicInscriptionTotalCount;
if (lastDynamicInscription.seed == 0 && _dynamicInscription[account].seed > 0)
++_dynamicInscriptionTotalCount;
emit OnDynamicInscriptionRemove(account, _dynamicInscription[account]);
}
function _addTokenToOwnerEnumeration(
address to,
SeedData memory data
) private {
if (to == _pool) return;
++_counts[to];
++_inscriptionsTotalCount;
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];
--_inscriptionsTotalCount;
_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 dynamicInscription(
address owner
) external view returns (SeedData memory data) {
return _dynamicInscription[owner];
}
function inscriptionCount(address owner) external view returns (uint) {
return _counts[owner];
}
function inscriptionOfOwnerByIndex(
address owner,
uint index
) external view returns (SeedData memory data) {
return _ownedTokens[owner][index];
}
function inscriptionsTotalCount() external view returns (uint) {
return _inscriptionsTotalCount;
}
function holdersCount() external view returns (uint) {
return _holdersCount;
}
function dynamicInscriptionTotalCount() external view returns (uint) {
return _dynamicInscriptionTotalCount;
}
function getHolderIndex(address account) external view returns (uint) {
return _holderListIndexes[account];
}
}
contract Fungi is Inscriptions, Generator, ReentrancyGuard {
uint constant _startTotalSupply = _TotalSupplyParam * (10 ** _decimals);
uint constant _startMaxBuyCount = (_startTotalSupply * 5) / 10000;
uint constant _addMaxBuyPercentPerSec = 5; // 100%=_addMaxBuyPrecision add 0.005%/second
uint constant _addMaxBuyPrecision = 100000;
constructor() {
_mint(msg.sender, _startTotalSupply);
}
modifier maxBuyLimit(uint256 amount) {
require(amount <= maxBuy());
_;
}
function maxBuy() public view returns (uint256) {
if (!isStarted()) return _startTotalSupply;
uint256 count = _startMaxBuyCount +
(_startTotalSupply *
(block.timestamp - _startTime) *
_addMaxBuyPercentPerSec) /
_addMaxBuyPrecision;
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);
}
// 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 bgOffset = 10000;
//uint constant bgCount = 1;
uint constant bgPaletteCount = 24;
uint constant colorCycleCount = 2;
uint constant eggOffset = 20000;
uint constant eggCount = 5;
uint constant eggPaletteCount = 62;
uint constant eggAnimationCount = 5;
uint constant bodyOffset = 30000;
uint constant bodyCount = 6;
uint constant bodyPaletteCount = 62;
uint constant expressionOffset = 40000;
uint constant expressionCount = 10;
uint constant expressionPaletteCount = 62;
uint constant eyesOffset = 50000;
uint constant eyesCount = 5;
uint constant eyesPaletteCount = 7;
uint constant hatOffset = 60000;
uint constant hatCount = 37;
uint constant hatPaletteCount = 8;
uint constant eyewearOffset = 70000;
uint constant eyewearCount = 23;
uint constant eyewearPaletteCount = 8;
uint constant clothesOffset = 80000;
uint constant clothesCount = 28;
uint constant clothesPaletteCount = 8;
uint constant animatedBgOffset = 90000;
uint constant animatedBg2Offset = 100000; // Two of these were too big so we split them in half
uint constant animatedBgCount = 7;
uint constant animatedBgPaletteCount = 9;
//uint constant pixelsCount = 100;
uint constant seedLevel1 = 1000; // egg that can have animated bg 10% of the time
//all frogs have base 5% chance of animated bg, 5% chance of body color cycling
uint constant seedLevel2 = 3000; // Naked frog - No Traits
uint constant seedLevel3 = 10000; // one trait 33% chance -
uint constant seedLevel4 = 30000; // two traits 33% per trait pair combination
uint constant seedLevel5 = 60000; // three traits 50% - 25% color cycling body
uint constant seedLevel6 = 120000; //chance of any combination three traits 50% chance per trait + 100% animated bg + 100% color cycling body
//string constant description = "";
//string constant web = "";
struct FrogData {
uint lvl;
uint egg;
uint eggPalette;
uint eggAnimation;
uint bg;
uint bgPalette;
uint colorCycle;
uint body;
uint bodyPalette;
bool animateBody;
uint expression;
uint eyes;
uint eyesPalette;
bool hatBool;
uint hat;
uint hatPalette;
bool eyewearBool;
uint eyewear;
uint eyewearPalette;
bool clothesBool;
uint clothes;
uint clothesPalette;
bool animateBg;
uint animatedBg;
uint animatedBgPalette;
}
struct Animation {
string keyFrame;
string keyFrameName;
string animation;
string[] targets;
}
struct FilePathData {
uint fileId;
Path[] Data;
}
struct Path {
uint16 color;
uint16 paletteIndex;
bool usePalette;
string d;
}
struct FilePaletteData {
uint16[] list;
}
struct SeedData {
uint seed;
uint extra;
}
struct Rand {
uint seed;
uint nonce;
uint extra;
}
library RandLib {
// This function throws if you query the deployer address before they receive any tokens
function next(Rand memory rnd) internal pure returns (uint) {
return
uint(
keccak256(
abi.encodePacked(rnd.seed + rnd.nonce++ -1, rnd.extra)
)
);
}
// rnd.seed is the integer token balace
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;
if (rnd.seed < seedLevel6) return 5;
return 6;
}
function random(
Rand memory rnd,
uint count
) internal pure returns (uint){
return next(rnd) % count;
}
}
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 SvgLib {
using SvgLib for Path;
using SvgLib for Animation[];
using RandLib for Rand;
using Converter for uint8;
using Converter for uint;
function toSvg(
Path memory p,
string memory color
) internal pure returns (string memory) {
return
string(
abi.encodePacked(
"<path stroke='#",
color,
"' d='",
p.d,
"'/>"
)
);
}
function toSvg(
Animation[] memory anim,
string memory uuid
) internal pure returns (string memory){
string memory res = string(abi.encodePacked(''));
string memory targetRes = string(abi.encodePacked(''));
string memory animationRes = string(abi.encodePacked('{ animation:'));
// Insert the keyframes
for (uint i = 0; i< anim.length; i++){
res = string(abi.encodePacked(res,' @keyframes ', anim[i].keyFrameName, '-',uuid, ' {', anim[i].keyFrame, '}'));
for (uint j = 0; j < anim[i].targets.length; j++){
// Setup the target resource, if j or i are greater than 0 then we insert a comman between targets
targetRes = string(abi.encodePacked(targetRes, (j>0 || i > 0? ',': ''), '#', 'froggi-', uuid, ' .', anim[i].targets[j]));
}
animationRes =string (abi.encodePacked(animationRes, anim[i].keyFrameName, '-', uuid,' ', anim[i].animation, ' infinite', (i==0 && anim.length >1?',': ';')));
}
res = string (abi.encodePacked(res,targetRes,animationRes,'transform-origin: bottom center;}'));
return res;
}
function toSvg(
Path[] storage paths,
mapping(uint256 => string) storage colors,
uint16[] storage traitColorMap,
string memory svgId
) internal view returns (string memory) {
string memory res = string(abi.encodePacked('<g class="', svgId, '">'));
for (uint i = 0; i < paths.length; ++i) {
if (paths[i].usePalette){
res = string(abi.encodePacked(res, paths[i].toSvg(colors[traitColorMap[paths[i].paletteIndex]])));
} else {
res = string(abi.encodePacked(res, paths[i].toSvg(colors[paths[i].color])));
}
}
res = string(abi.encodePacked(res, '</g>'));
return res;
}
}
contract Generator is Ownable {
using SvgLib for Path;
using SvgLib for Path[];
using SvgLib for Animation[];
using RandLib for Rand;
using Converter for uint;
mapping(uint => Path[]) paths;
mapping(uint => uint16[]) palettes;
mapping(uint => string) colors;
mapping(uint => Animation) animations;
constructor() {
}
// Data insertion functions
function setPaths(FilePathData[] calldata data, uint offset) external onlyOwner {
for (uint i = 0; i < data.length; ++i) {
// We had to remove filepathdata to memory here as dynamic arrays in struct aren't supported yet
Path[] storage storageFile = paths[offset + data[i].fileId];
for (uint j = 0; j < data[i].Data.length; ++j) {
storageFile.push(data[i].Data[j]);
}
}
}
function setPalettes(FilePaletteData[] calldata data, uint offset) external onlyOwner {
for (uint i = 0; i < data.length; ++i) {
// We had to remove filepathdata to memory here as dynamic arrays in struct aren't supported yet
uint16[] storage storageFile = palettes[offset + i];
for (uint j = 0; j < data[i].list.length; ++j) {
storageFile.push(data[i].list[j]);
}
}
}
function setColors(string[] calldata data, uint offset) external onlyOwner {
for (uint i = 0; i < data.length; ++i) {
colors[offset + i] = data[i];
}
}
function setAnimation(Animation calldata data, uint id) external onlyOwner {
animations[id] = data;
}
// Sets frog traits based on level
function setTraits(FrogData memory data, Rand memory rnd) internal pure {
// all levels have a bg, but might not use this one
//data.bg = rnd.random(bgCount); theres only one bg we dont need this
data.bgPalette = rnd.random(bgPaletteCount);
data.colorCycle = rnd.random(colorCycleCount);
if (data.lvl <= 1) { // Lvl 0 is the egg, so we dont have to do the rest of the rng stuff
data.egg = rnd.random(eggCount);
data.eggPalette = rnd.random(eggPaletteCount);
data.eggAnimation = rnd.random(eggAnimationCount) + 2;
if (data.lvl == 1){
data.animateBg = (rnd.random(20) < 1 ? true: false); // 5% chance
}
} else {
// All level 1 have a 5% chance to have color cycling or animated bg
data.animateBg = (rnd.random(20) < 1 ? true: false);
data.animateBody = (rnd.random(20) < 1 ? true: false);
uint accessory = rnd.random(3);
if (data.lvl == 3){ // 33% chance to have any trait
if (accessory == 0){
data.eyewearBool = true;
} else if (accessory == 1){
data.hatBool = true;
} else if (accessory == 2){
data.clothesBool = true;
}
data.animateBody = (rnd.random(10)<1? true: false); // 10%
}else if (data.lvl == 4){ // 33 % to have two of any trait
if (accessory == 0){
data.eyewearBool = true;
data.hatBool = true;
} else if (accessory == 1){
data.hatBool = true;
data.clothesBool = true;
} else if (accessory == 2){
data.eyewearBool = true;
data.clothesBool = true;
}
data.animateBody = (rnd.random(5) < 1 ? true: false); // 20%
} else if (data.lvl >= 5){ // 50% chance to have any trait
data.hatBool = (rnd.random(2) == 1 ? true: false);
data.eyewearBool = (rnd.random(2) == 1 ? true: false);
data.clothesBool = (rnd.random(2) == 1 ? true: false);
data.animateBody = ((rnd.random(4) < 1 || data.lvl == 6) ? true: false); //25%
data.animateBg = ((rnd.random(4) < 1 || data.lvl == 6) ? true: false); // 25%
}
data.body = rnd.random(bodyCount);
data.bodyPalette = rnd.random(bodyPaletteCount);
data.expression = rnd.random(5) + (data.body <= 2 ? 0 : 5);
data.eyes = rnd.random(eyesCount);
data.eyesPalette = rnd.random(eyesPaletteCount);
if (data.eyewearBool) {
data.eyewear = rnd.random(eyewearCount);
data.eyewearPalette = rnd.random(eyewearPaletteCount);
}
if (data.hatBool) {
data.hat = rnd.random(hatCount);
data.hatPalette = rnd.random(hatPaletteCount);
}
if (!data.hatBool && !data.eyewearBool && data.clothesBool){ // This enables astronaut suit
data.clothes = rnd.random(clothesCount);
data.clothesPalette = rnd.random(clothesPaletteCount);
} else if (data.clothesBool) { // This removes astronaut suit from options if theres either a hat or eyewear
data.clothes = rnd.random((clothesCount - 3));
data.clothesPalette = rnd.random(clothesPaletteCount);
}
}
if (data.animateBg){
data.animatedBg = rnd.random(animatedBgCount);
data.animatedBgPalette = rnd.random(animatedBgPaletteCount);
}
}
function getFrog(
SeedData calldata seed_data
) external pure returns (FrogData memory) {
require(seed_data.seed >= 1);
Rand memory rnd = Rand(seed_data.seed, 0, seed_data.extra);
FrogData memory data;
data.lvl = rnd.lvl();
setTraits(data, rnd);
return data;
}
function getSvg(
SeedData calldata seed_data
) external view returns (string memory) {
string memory uuid = getUuid(seed_data);
return toSvg(this.getFrog(seed_data), uuid);
}
function toSvg(
FrogData memory data,
string memory uuid
) private view returns (string memory) {
// We hard code the viewbox here to save some bytes.
bytes memory svgStart = abi.encodePacked(
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' shape-rendering='crispEdges' id='froggi-", uuid,"'>"
);
bytes memory bg = (data.animateBg ? abi.encodePacked(getSvgPart(data, animatedBgOffset), getSvgPart(data,animatedBg2Offset)): abi.encodePacked(getSvgPart(data, bgOffset)));
bytes memory animatedBg = abi.encodePacked((data.animateBg ? getSvgRainbowScript(data, uuid): ''));
if (data.lvl <= 1) {
Animation[] memory anim = new Animation[](2);
anim[0] = animations[data.colorCycle]; // Eggs always have color cycling and another animation for their wobble
anim[1] = animations[data.eggAnimation];
return
string(
abi.encodePacked(
svgStart,
bg,
getSvgPart(data, eggOffset),
'<style>',
anim.toSvg(uuid),
animatedBg,
'</style></svg>'
)
);
} else {
Animation[] memory anim = new Animation[](1);
anim[0] = animations[data.colorCycle]; // In the future we could support multiple animations this way
//getSvgRainbowScript(data, uuid);
return
string(
abi.encodePacked(
svgStart,
bg,
getSvgPart(data, bodyOffset),
getSvgPart(data, expressionOffset),
getSvgPart(data, eyesOffset),
getSvgPart(data, hatOffset),
getSvgPart(data, eyewearOffset),
getSvgPart(data, clothesOffset),
'<style>',
animatedBg,
(data.animateBody ? anim.toSvg(uuid): ''),
'</style></svg>'
)
);
}
}
// Im going to be honest. I am not proud of this implementation but it was the only script that needed this logic
function getSvgRainbowScript(FrogData memory data, string memory uuid) internal view returns (string memory res){
Path[] memory rainbowPaths = paths[90000 + data.animatedBg];
for (uint j; j < 2; j++){
uint16[] memory traitColors = palettes[(j==0? 90000: 100000) + data.animatedBgPalette];
string[] memory rainbow = new string[](rainbowPaths.length);
for (uint i; i < rainbow.length; i++){
rainbow[i] = colors[traitColors[rainbowPaths[i].paletteIndex]];
}
res = string(abi.encodePacked(res,
'@keyframes cycle-rainbow-',uuid,
'{0%,100%{stroke: #', rainbow[0],
';}8.33%{stroke: #', rainbow[1],
';}16.66%{stroke: #',rainbow[2],
';}25%{stroke: #',rainbow[3],
';}33%{stroke: #',rainbow[4],
';}41.66%{stroke: #',rainbow[5],
';}50%{stroke: #',rainbow[6],
';}58.33%{stroke: #',rainbow[7],
';}66.66%{stroke: #',rainbow[8],
';}75%{stroke: #',rainbow[9],
';}83.33%{stroke: #',rainbow[10],'}}'
));
res = string(abi.encodePacked(res, '#froggi-', uuid, ' .animated-bg path{animation:cycle-rainbow-',uuid,' 2s step-start infinite;}'));
for (uint i; i< rainbow.length; i++){
res = string(abi.encodePacked(res, '#froggi-', uuid, ' .animated-bg path[stroke="#', rainbow[i],'"]{animation-delay: -.', i.toString(), 's;}'));
}
}
return res;
}
function getSvgPart(
FrogData memory seed_data,
uint partOffset
) private view returns (string memory) {
uint partId;
uint palette;
string memory svgId;
if (partOffset == eggOffset) {
partId = seed_data.egg;
palette = seed_data.eggPalette;
svgId = 'egg';
} else if (partOffset == bgOffset){
partId = seed_data.bg;
palette = seed_data.bgPalette;
svgId = 'bg';
} else if (partOffset == bodyOffset){
partId = seed_data.body;
palette = seed_data.bodyPalette;
svgId = 'body';
} else if (partOffset == expressionOffset){
partId = seed_data.expression;
palette = seed_data.bodyPalette;// We use body for expression
svgId = 'expression';
} else if (partOffset == eyesOffset){
partId = seed_data.eyes;
palette = seed_data.eyesPalette;
svgId = 'eyes';
} else if (partOffset == hatOffset){
partId = seed_data.hat;
palette = seed_data.hatPalette;
svgId = 'hat';
} else if (partOffset == eyewearOffset){
partId = seed_data.eyewear;
palette = seed_data.eyewearPalette;
svgId = 'eyewear';
} else if (partOffset == clothesOffset){
partId = seed_data.clothes;
palette = seed_data.clothesPalette;
svgId = 'clothes';
} else if (partOffset == animatedBgOffset || partOffset == animatedBg2Offset) {
partId = seed_data.animatedBg;
palette = seed_data.animatedBgPalette;
svgId = 'animated-bg';
}
return paths[partOffset + partId].toSvg(colors, palettes[partOffset + palette], svgId);
}
function getUuid(
SeedData calldata seed_data
) internal pure returns (string memory) {
return string(abi.encodePacked(seed_data.seed.toString(), seed_data.extra.toString()));
}
function getMeta(
SeedData calldata seed_data
) external view returns (string memory) {
FrogData memory data = this.getFrog(seed_data);
bytes memory lvl = abi.encodePacked('"level":', data.lvl.toString());
bytes memory bg = abi.encodePacked(
',"bg":"',
data.bg.toString(),
'","bgPalette":"',
data.bgPalette.toString(),
'"'
);
bytes memory egg = abi.encodePacked(
',"egg":"',
data.egg.toString(),
'","eggPalette":"',
data.eggPalette.toString(),
'","eggAnimation":"',
data.eggAnimation.toString(),
'"'
);
bytes memory body = abi.encodePacked(
',"body":"',
data.body.toString(),
'","bodyPalette":"',
data.bodyPalette.toString(),
'","expression":"',
data.expression.toString(),
'","animateBody":"',
(data.animateBody ? 'true': 'false'),
'","colorCycle":"',
data.colorCycle.toString(),
'"'
);
bytes memory eyes = abi.encodePacked(
',"eyes":"',
data.eyes.toString(),
'","eyesPalette":"',
data.eyesPalette.toString(),
'"'
);
bytes memory hat = abi.encodePacked(
',"hat":"',
data.hat.toString(),
'","hatPalette":"',
data.hatPalette.toString(),
'","hatBool":"',
(data.hatBool ? 'true': 'false'),
'"'
);
bytes memory eyewear = abi.encodePacked(
',"eyewear":"',
data.eyewear.toString(),
'","eyewearPalette":"',
data.eyewearPalette.toString(),
'","eyewearBool":"',
(data.eyewearBool ? 'true': 'false'),
'"'
);
bytes memory clothes = abi.encodePacked(
',"clothes":"',
data.clothes.toString(),
'","clothesPalette":"',
data.clothesPalette.toString(),
'","clothesBool":"',
(data.clothesBool ? 'true': 'false'),
'"'
);
bytes memory animatedBg = abi.encodePacked(
',"animatedBg":"',
data.animatedBg.toString(),
'","animatedBgPalette":"',
data.animatedBgPalette.toString(),
'","animateBg":"',
(data.animateBg ? 'true': 'false'),
'"'
);
bytes memory web_text = abi.encodePacked(',"web":"https://froggi.net/",');
bytes memory description_text = abi.encodePacked(
'"description":"Froggi, $froggi."'
);
return
string(
abi.encodePacked(
"{",
lvl,
bg,
egg,
body,
eyes,
hat,
eyewear,
clothes,
animatedBg,
web_text,
description_text,
"}"
)
);
}
// function getSvgDEBUG(
// FrogData calldata frog_data,
// string memory uuid
// ) external view returns (string memory) {
// return toSvg(frog_data, uuid);
// }
}
// 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);
_;
}
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 {
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": [],
"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":"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":"OnDynamicInscriptionAdd","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":"OnDynamicInscriptionRemove","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":"OnInscriptionTransfer","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":"address","name":"owner","type":"address"}],"name":"dynamicInscription","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":"dynamicInscriptionTotalCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":"getFrog","outputs":[{"components":[{"internalType":"uint256","name":"lvl","type":"uint256"},{"internalType":"uint256","name":"egg","type":"uint256"},{"internalType":"uint256","name":"eggPalette","type":"uint256"},{"internalType":"uint256","name":"eggAnimation","type":"uint256"},{"internalType":"uint256","name":"bg","type":"uint256"},{"internalType":"uint256","name":"bgPalette","type":"uint256"},{"internalType":"uint256","name":"colorCycle","type":"uint256"},{"internalType":"uint256","name":"body","type":"uint256"},{"internalType":"uint256","name":"bodyPalette","type":"uint256"},{"internalType":"bool","name":"animateBody","type":"bool"},{"internalType":"uint256","name":"expression","type":"uint256"},{"internalType":"uint256","name":"eyes","type":"uint256"},{"internalType":"uint256","name":"eyesPalette","type":"uint256"},{"internalType":"bool","name":"hatBool","type":"bool"},{"internalType":"uint256","name":"hat","type":"uint256"},{"internalType":"uint256","name":"hatPalette","type":"uint256"},{"internalType":"bool","name":"eyewearBool","type":"bool"},{"internalType":"uint256","name":"eyewear","type":"uint256"},{"internalType":"uint256","name":"eyewearPalette","type":"uint256"},{"internalType":"bool","name":"clothesBool","type":"bool"},{"internalType":"uint256","name":"clothes","type":"uint256"},{"internalType":"uint256","name":"clothesPalette","type":"uint256"},{"internalType":"bool","name":"animateBg","type":"bool"},{"internalType":"uint256","name":"animatedBg","type":"uint256"},{"internalType":"uint256","name":"animatedBgPalette","type":"uint256"}],"internalType":"struct FrogData","name":"","type":"tuple"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getHolderByIndex","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getHolderIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":"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"}],"name":"inscriptionCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"inscriptionOfOwnerByIndex","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":"inscriptionsTotalCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","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":"nonpayable","type":"function"},{"inputs":[],"name":"maxBuy","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":"string","name":"keyFrame","type":"string"},{"internalType":"string","name":"keyFrameName","type":"string"},{"internalType":"string","name":"animation","type":"string"},{"internalType":"string[]","name":"targets","type":"string[]"}],"internalType":"struct Animation","name":"data","type":"tuple"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"setAnimation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string[]","name":"data","type":"string[]"},{"internalType":"uint256","name":"offset","type":"uint256"}],"name":"setColors","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint16[]","name":"list","type":"uint16[]"}],"internalType":"struct FilePaletteData[]","name":"data","type":"tuple[]"},{"internalType":"uint256","name":"offset","type":"uint256"}],"name":"setPalettes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"fileId","type":"uint256"},{"components":[{"internalType":"uint16","name":"color","type":"uint16"},{"internalType":"uint16","name":"paletteIndex","type":"uint16"},{"internalType":"bool","name":"usePalette","type":"bool"},{"internalType":"string","name":"d","type":"string"}],"internalType":"struct Path[]","name":"Data","type":"tuple[]"}],"internalType":"struct FilePathData[]","name":"data","type":"tuple[]"},{"internalType":"uint256","name":"offset","type":"uint256"}],"name":"setPaths","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"}]