账户
0x88...22fe
0x88...22FE

0x88...22FE

$500
此合同的源代码已经过验证!
合同元数据
编译器
0.8.24+commit.e11b9ed9
语言
Solidity
合同源代码
文件 1 的 9:Context.sol
// 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;
    }
}
合同源代码
文件 2 的 9:Erc20.sol
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);
            }
        }
    }
}
合同源代码
文件 3 的 9:Fungi.sol
// 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();
    }
}
合同源代码
文件 4 的 9:Generator.sol
// 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);
    // }
}
合同源代码
文件 5 的 9:IERC20.sol
// 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);
}
合同源代码
文件 6 的 9:IERC20Metadata.sol
// 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);
}
合同源代码
文件 7 的 9:Ownable.sol
// 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;
    }
}
合同源代码
文件 8 的 9:PoolCreatableErc20i.sol
// 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;
    }
}
合同源代码
文件 9 的 9:ReentrancyGuard.sol
// 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
}
ABI
[{"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"}]