Blockchain / Global / Address

Contract

0x5de50FF0A6Ac3B9f6F7beE2e72EcadAa3a718705

Balance
0 SOMI
Transactions sent
1
Type
Contract · 24,521 bytes of code

SomniaFarming Verified

Compiler
solc 0.8.24+commit.e11b9ed9
Language
Solidity
Optimization
Yes, 200 runs
EVM version
cancun
License
—
Verified
2026-10-12

contracts/SomniaFarming.sol

and 5 more files

Solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract SomniaFarming is Ownable, Pausable, ReentrancyGuard {
    // --- Structs ---
    struct Item {
        uint256 id;
        string name;
        uint8 itemType; // 0=seed, 1=tool, 2=upgrade
        uint256 priceETH;
        address paymentToken;
        uint256 baseReward; // RISE earned on harvest (base yield)
        uint256 baseGrowthTime; // seconds until harvest (base)
        uint16 growthBonusBP; // growth speed bonus in basis points (e.g. 15 = 1.5%, 70 = 7%)
        uint16 yieldBonusBP; // yield bonus in basis points (e.g. 15 = 1.5%, 70 = 7%)
        bool active;
        uint256 maxSupply; // Maximum supply for this item
        uint256 supply;    // Current supply of this item
    }
    struct Bundle {
        uint256 id;
        string name;
        uint256[] itemIds;
        uint256[] itemAmounts;
        uint256 priceETH;
        address paymentToken;
        bool active;
        uint256 maxSupply;
        uint256 supply;
    }
    enum PlotState { Empty, NeedsWater, Growing, Ready, Locked }
    struct Plot {
        uint256 seedId;
        uint256 plantedAt;
        uint256 readyAt;
        uint16 growthBonusBP;
        uint16 yieldBonusBP;
        bool growing;
        uint256 harvestedAt;
        bool needsFertilizer;
        bool startedGrowing;
        PlotState state;
    }

    // --- Storage ---
    mapping(uint256 => Item) public items;
    mapping(uint256 => Bundle) public bundles;
    mapping(address => mapping(uint256 => uint256)) public userItemBalances; // user => itemId => balance
    mapping(address => mapping(uint256 => Plot)) public userPlots; // user => plotId => Plot
    mapping(address => uint256) public userStreaks;
    mapping(address => uint256) public lastClaimedDay;
    mapping(address => uint256) public playerLevel;
    mapping(address => uint256) public totalXP;
    mapping(address => uint256) public farmLevel;
    mapping(address => uint256) public totalHarvests;
    mapping(address => uint256) public riceTokens; // On-chain RISE balance
    mapping(address => bool) public hasAutoWatering;
    // Add quest tracking
    mapping(address => mapping(uint256 => bool)) public claimedQuests; // user => questId => claimed
    mapping(uint256 => uint256) public questRewards; // questId => reward amount

    // Payment tokens
    mapping(address => bool) public acceptedPaymentTokens; // 0x0 for ETH, or ERC20 address

    // Daily rewards
    uint256[7] public dailyRewards; // 1 RT to 7 RT

    IERC20 public riceToken;
    // Burn address for claim fee
    address public constant BURN_ADDRESS = 0x000000000000000000000000000000000000dEaD;
    
    // Minimum RT threshold required before a user can claim ERC20 RT
    uint256 public minClaimRT = 500;

    uint16 public constant MIN_BONUS_BP = 0; // 0% now allowed for single seeds
    uint16 public constant MAX_BONUS_BP = 500; // 50% to accommodate bundle bonuses

    // Add item IDs for harvester and energy booster
    uint256 public constant HARVESTER_ID = 7;
    uint256 public constant ENERGY_BOOSTER_ID = 19;

    // Add Basic Rice Seed (Single)
    uint256 public constant BASIC_SEED_SINGLE_ID = 9;
    // Add Premium Rice Seed (Single)
    uint256 public constant PREMIUM_SEED_SINGLE_ID = 10;
    // Add Hybrid Rice Seed (Single)
    uint256 public constant HYBRID_SEED_SINGLE_ID = 11;
    // Add Fertilizer Spreader
    uint256 public constant FERTILIZER_SPREADER_ID = 12;
    // Add Starter Bundle (as an item for supply tracking)
    uint256 public constant STARTER_BUNDLE_ID = 4;

    // Add Watering Can
    uint256 public constant WATERING_CAN_ID = 5;

    // Add Golden Harvester (Single & Bundle)
    uint256 public constant GOLDEN_HARVESTER_SINGLE_ID = 17;
    uint256 public constant GOLDEN_HARVESTER_BUNDLE_ID = 18;

    // Add tool durability mapping
    mapping(address => mapping(uint256 => uint256)) public userToolUses;

    // --- Secondary Marketplace (internal) ---
    struct Listing {
        uint256 id;
        address seller;
        uint256 itemId;
        uint256 amount; // remaining amount available
        uint256 priceETH; // price per unit in wei
        bool active;
    }
    mapping(uint256 => Listing) public listings;
    uint256 public nextListingId;
    uint16 public marketplaceFeeBP; // e.g., 200 = 2%
    address public treasury;

    // --- Events ---
    event ItemPurchased(address indexed user, uint256 bundleId, uint256[] itemIds, uint256[] amounts, address paymentToken, uint256 price);
    event SeedPlanted(address indexed user, uint256 plotId, uint256 seedId);
    event Harvested(address indexed user, uint256 plotId, uint256 reward);
    event DailyRewardClaimed(address indexed user, uint256 day, uint256 reward);
    event ToolUsed(address indexed user, uint256 toolId, uint256 plotId);
    event FarmUpgraded(address indexed user, uint256 upgradeId);
    event PaymentTokenAdded(address token);
    event PaymentTokenRemoved(address token);
    event RiceClaimed(address indexed user, uint256 amount);
    event BundlePurchased(address indexed user, uint256 bundleId, uint256[] itemIds, uint256[] amounts, uint256 price);
    event QuestRewardClaimed(address indexed user, uint256 questId, uint256 reward);
    event ItemPurchasedWithRT(address indexed user, uint256 itemId, uint256 amount, uint256 totalRT);
    event PlotReady(address indexed user, uint256 plotId);


    event ToolBurned(address indexed user, uint256 toolId);
    // Marketplace events
    event Listed(uint256 indexed listingId, address indexed seller, uint256 itemId, uint256 amount, uint256 priceETH);
    event Cancelled(uint256 indexed listingId);
    event Bought(uint256 indexed listingId, address indexed buyer, uint256 amount, uint256 totalPaid, uint256 fee, uint256 sellerProceeds);
    event EnergyGiven(address indexed user, uint256 energyAmount, uint256 timestamp);

    // --- Storage ---
    mapping(address => mapping(uint256 => uint256)) public userSingleSeeds;
    mapping(address => mapping(uint256 => uint256)) public userBundleSeeds;

    // Add a pause flag for daily rewards
    bool public dailyRewardPaused = false;

    // --- Energy System ---
    mapping(address => uint256) public userEnergy;
    mapping(address => bool) public hasClaimedInitialEnergy;



    // --- Admin Functions ---
    function addItem(Item memory item) external onlyOwner {
        items[item.id] = item;
    }
    function updateItem(Item memory item) external onlyOwner {
        require(items[item.id].id != 0, "Item does not exist");
        items[item.id] = item;
    }
    function deactivateItem(uint256 itemId) external onlyOwner {
        items[itemId].active = false;
    }
    function addBundle(Bundle memory bundle) external onlyOwner {
        bundles[bundle.id] = bundle;
    }
    function updateBundle(Bundle memory bundle) external onlyOwner {
        require(bundles[bundle.id].id != 0, "Bundle does not exist");
        bundles[bundle.id] = bundle;
    }
    function deactivateBundle(uint256 bundleId) external onlyOwner {
        bundles[bundleId].active = false;
    }
    function addPaymentToken(address token) external onlyOwner {
        acceptedPaymentTokens[token] = true;
        emit PaymentTokenAdded(token);
    }
    function removePaymentToken(address token) external onlyOwner {
        acceptedPaymentTokens[token] = false;
        emit PaymentTokenRemoved(token);
    }
    function pause() external onlyOwner {
        _pause();
    }
    function unpause() external onlyOwner {
        _unpause();
    }
    function withdrawETH() external onlyOwner {
        (bool sent, ) = msg.sender.call{value: address(this).balance}("");
        require(sent, "Withdraw failed");
    }
    function withdrawToken(address token) external onlyOwner {
        uint256 bal = IERC20(token).balanceOf(address(this));
        require(bal > 0, "No balance");
        IERC20(token).transfer(msg.sender, bal);
    }
    function setRiceToken(address _riceToken) external onlyOwner {
        riceToken = IERC20(_riceToken);
    }
    function setItemMaxSupply(uint256 itemId, uint256 maxSupply) external onlyOwner {
        require(items[itemId].id != 0, "Item does not exist");
        items[itemId].maxSupply = maxSupply;
        items[itemId].supply = maxSupply;
    }

    // --- Constructor ---
    constructor() Ownable(msg.sender) {
        acceptedPaymentTokens[address(0)] = true;
        dailyRewards = [1, 1, 1, 1, 1, 1, 1]; // All days: 1 RT

        // Marketplace defaults
        marketplaceFeeBP = 200; // 2%
        treasury = msg.sender;

        // Initialize quest rewards
        questRewards[1] = 1; // 1 RT for planting 3 seeds
        questRewards[2] = 2; // 2 RT for watering 5 plots
        questRewards[3] = 3; // 3 RT for harvesting 2 plots

        // Update Basic Rice Seed (Single) - 15x multiplier (5x * 3x)
        items[BASIC_SEED_SINGLE_ID] = Item({
            id: BASIC_SEED_SINGLE_ID,
            name: "Basic Rice Seed (Single)",
            itemType: 0,
            priceETH: 250000000000000000, // 0.25 SOMI
            paymentToken: address(0),
            baseReward: 15,
            baseGrowthTime: 172800, // 48 hours
            growthBonusBP: 0,
            yieldBonusBP: 0,
            active: true,
            maxSupply: 150000, // 50000 * 3
            supply: 150000     // 50000 * 3
        });
        // Update Basic Rice Seed (Bundle) - 40% bonus (5 seeds per bundle)
        bundles[13] = Bundle({
            id: 13,
            name: "Basic Rice Seed (Bundle)",
            itemIds: new uint256[](1),
            itemAmounts: new uint256[](1),
            priceETH: 300000000000000000, // 0.3 SOMI
            paymentToken: address(0),
            active: true,
            maxSupply: 15000, // 5000 * 3
            supply: 15000     // 5000 * 3
        });
        bundles[13].itemIds[0] = BASIC_SEED_SINGLE_ID;
        bundles[13].itemAmounts[0] = 5;
        items[BASIC_SEED_SINGLE_ID].growthBonusBP = 400; // 40% bonus (matches plantSeed function)
        items[BASIC_SEED_SINGLE_ID].yieldBonusBP = 400;  // 40% bonus (matches plantSeed function)

        // Update Premium Rice Seed (Single) - 15x multiplier (5x * 3x)
        items[PREMIUM_SEED_SINGLE_ID] = Item({
            id: PREMIUM_SEED_SINGLE_ID,
            name: "Premium Rice Seed (Single)",
            itemType: 0,
            priceETH: 320000000000000000, // 0.32 SOMI
            paymentToken: address(0),
            baseReward: 50,
            baseGrowthTime: 259200, // 72 hours
            growthBonusBP: 0,
            yieldBonusBP: 0,
            active: true,
            maxSupply: 60000, // 20000 * 3
            supply: 60000     // 20000 * 3
        });
        // Update Premium Rice Seed (Bundle) - 20% bonus (2 seeds per bundle)
        bundles[14] = Bundle({
            id: 14,
            name: "Premium Rice Seed (Bundle)",
            itemIds: new uint256[](1),
            itemAmounts: new uint256[](1),
            priceETH: 350000000000000000, // 0.35 SOMI
            paymentToken: address(0),
            active: true,
            maxSupply: 15000, // 5000 * 3
            supply: 15000     // 5000 * 3
        });
        bundles[14].itemIds[0] = PREMIUM_SEED_SINGLE_ID;
        bundles[14].itemAmounts[0] = 2;
        items[PREMIUM_SEED_SINGLE_ID].growthBonusBP = 200; // 20% bonus (matches plantSeed function)
        items[PREMIUM_SEED_SINGLE_ID].yieldBonusBP = 200;  // 20% bonus (matches plantSeed function)

        // Update Hybrid Rice Seed (Single) - 15x multiplier (5x * 3x)
        items[HYBRID_SEED_SINGLE_ID] = Item({
            id: HYBRID_SEED_SINGLE_ID,
            name: "Hybrid Rice Seed (Single)",
            itemType: 0,
            priceETH: 500000000000000000, // 0.5 SOMI
            paymentToken: address(0),
            baseReward: 70,
            baseGrowthTime: 345600, // 96 hours
            growthBonusBP: 0,
            yieldBonusBP: 0,
            active: true,
            maxSupply: 30000, // 10000 * 3
            supply: 30000     // 10000 * 3
        });
        // Update Hybrid Rice Seed (Bundle) - 21.4% bonus (2 seeds per bundle)
        bundles[15] = Bundle({
            id: 15,
            name: "Hybrid Rice Seed (Bundle)",
            itemIds: new uint256[](1),
            itemAmounts: new uint256[](1),
            priceETH: 600000000000000000, // 0.6 SOMI
            paymentToken: address(0),
            active: true,
            maxSupply: 7500, // 2500 * 3
            supply: 7500     // 2500 * 3
        });
        bundles[15].itemIds[0] = HYBRID_SEED_SINGLE_ID;
        bundles[15].itemAmounts[0] = 2;
        items[HYBRID_SEED_SINGLE_ID].baseReward = 85;
        items[HYBRID_SEED_SINGLE_ID].growthBonusBP = 214; // 21.4% bonus (matches plantSeed function)
        items[HYBRID_SEED_SINGLE_ID].yieldBonusBP = 214;  // 21.4% bonus (matches plantSeed function)

        // Golden Harvester (Single) - RT priced via buyItemWithGameRT
        items[GOLDEN_HARVESTER_SINGLE_ID] = Item({
            id: GOLDEN_HARVESTER_SINGLE_ID,
            name: "Golden Harvester (Single)",
            itemType: 1,
            priceETH: 0, // RT-priced (25 RT per item via buyItemWithGameRT)
            paymentToken: address(0),
            baseReward: 0,
            baseGrowthTime: 0,
            growthBonusBP: 0,
            yieldBonusBP: 0,
            active: true,
            maxSupply: 56250, // 18750 * 3
            supply: 56250     // 18750 * 3
        });
        // Golden Harvester (Bundle) - 15x multiplier (5x * 3x)
        bundles[16] = Bundle({
            id: 16,
            name: "Golden Harvester (Bundle)",
            itemIds: new uint256[](1),
            itemAmounts: new uint256[](1),
            priceETH: 300000000000000000, // 0.3 SOMI
            paymentToken: address(0),
            active: true,
            maxSupply: 28125, // 9375 * 3
            supply: 28125     // 9375 * 3
        });
        bundles[16].itemIds[0] = GOLDEN_HARVESTER_SINGLE_ID;
        bundles[16].itemAmounts[0] = 2;
        // Add Fertilizer Spreader
        items[FERTILIZER_SPREADER_ID] = Item({
            id: FERTILIZER_SPREADER_ID,
            name: "Fertilizer Spreader",
            itemType: 1, // tool
            priceETH: 0, // Set actual price as needed
            paymentToken: address(0),
            baseReward: 0,
            baseGrowthTime: 0,
            growthBonusBP: 25,
            yieldBonusBP: 15,
            active: true,
            maxSupply: 10000,
            supply: 10000
        });
        // Add Starter Bundle (as an item for supply tracking)
        items[STARTER_BUNDLE_ID] = Item({
            id: STARTER_BUNDLE_ID,
            name: "Starter Bundle",
            itemType: 0, // or 3 if you want a new type for bundles
            priceETH: 0, // Set actual price as needed
            paymentToken: address(0),
            baseReward: 0,
            baseGrowthTime: 0,
            growthBonusBP: 0,
            yieldBonusBP: 0,
            active: true,
            maxSupply: 1000,
            supply: 1000
        });

        // Add Watering Can
        items[WATERING_CAN_ID] = Item({
            id: WATERING_CAN_ID,
            name: "Watering Can",
            itemType: 1, // tool
            priceETH: 20000000000000000, // 0.02 SOMI
            paymentToken: address(0),
            baseReward: 0,
            baseGrowthTime: 0,
            growthBonusBP: 0,
            yieldBonusBP: 0,
            active: true,
            maxSupply: 300000,
            supply: 300000
        });

        // Energy Booster
        items[ENERGY_BOOSTER_ID] = Item({
            id: ENERGY_BOOSTER_ID,
            name: "Energy Booster",
            itemType: 2,
            priceETH: 0, // RT-priced via buyItemWithGameRT
            paymentToken: address(0),
            baseReward: 0,
            baseGrowthTime: 0,
            growthBonusBP: 0,
            yieldBonusBP: 0,
            active: true,
            maxSupply: 100000,
            supply: 100000
        });
    }

    // --- Marketplace ---
    function buyBundle(uint256 bundleId, uint256 amount, address paymentToken) external payable whenNotPaused nonReentrant {
        Bundle storage bundle = bundles[bundleId];
        require(bundle.active, "Bundle inactive");
        require(bundle.supply >= amount, "Insufficient bundle supply");
        uint256 totalPrice = paymentToken == address(0) ? bundle.priceETH * amount : 0; // Only ETH for now
        if (paymentToken == address(0)) {
            require(msg.value >= totalPrice, "Insufficient ETH");
        } else {
            IERC20(paymentToken).transferFrom(msg.sender, address(this), totalPrice);
        }
        // Check and decrement supply for each item in the bundle
        for (uint256 i = 0; i < bundle.itemIds.length; i++) {
            uint256 itemId = bundle.itemIds[i];
            uint256 itemAmount = bundle.itemAmounts[i] * amount;
            require(items[itemId].supply >= itemAmount, "Insufficient supply");
            items[itemId].supply -= itemAmount;
            // If seed, increment bundle seed count
            if (items[itemId].itemType == 0) {
                userBundleSeeds[msg.sender][itemId] += itemAmount;
            }
            // In buyBundle, for each item in the bundle:
            if (items[itemId].itemType == 1) {
                // Assign tool uses per specific tool type
                uint256 usesPerItem = 5;
                if (itemId == GOLDEN_HARVESTER_SINGLE_ID) {
                    usesPerItem = 2; // Single harvester: 2 uses per item
                } else if (itemId == GOLDEN_HARVESTER_BUNDLE_ID) {
                    usesPerItem = 4; // Bundle harvester tool: 4 uses per item
                } else if (itemId == FERTILIZER_SPREADER_ID) {
                    usesPerItem = 3; // Fertilizer: 3 uses per item
                }
                userToolUses[msg.sender][itemId] += usesPerItem * itemAmount;
            }
        }
        // Decrement bundle supply
        bundle.supply -= amount;
        // Credit items
        for (uint256 i = 0; i < bundle.itemIds.length; i++) {
            userItemBalances[msg.sender][bundle.itemIds[i]] += bundle.itemAmounts[i] * amount;
        }
        emit BundlePurchased(msg.sender, bundleId, bundle.itemIds, bundle.itemAmounts, totalPrice);
        // Award XP for buying harvester bundle, else 1 XP
        if (bundleId == GOLDEN_HARVESTER_BUNDLE_ID) {
            totalXP[msg.sender] += 5;
        } else {
            totalXP[msg.sender] += 1;
        }
    }

    function buyItem(uint256 itemId, uint256 amount, address paymentToken) external payable whenNotPaused nonReentrant {
        Item storage item = items[itemId];
        require(item.active, "Item inactive");
        require(item.supply >= amount, "Insufficient supply");
        require(amount > 0, "Amount must be > 0");
        // Disallow ETH path for RT-priced items (priceETH == 0)
        require(item.priceETH > 0, "Use RT purchase");
        uint256 totalPrice = paymentToken == address(0) ? item.priceETH * amount : 0; // Only ETH for now
        if (paymentToken == address(0)) {
            require(msg.value >= totalPrice, "Insufficient ETH");
        } else {
            IERC20(paymentToken).transferFrom(msg.sender, address(this), totalPrice);
        }
        // Decrement supply
        item.supply -= amount;
        // Credit item to user
        userItemBalances[msg.sender][itemId] += amount;
        // If seed, increment single seed count
        if (item.itemType == 0) {
            userSingleSeeds[msg.sender][itemId] += amount;
        }
        // In buyItem, when a user receives a tool, set specific uses per tool
        if (item.itemType == 1) {
            uint256 usesPerItem = 5;
            if (itemId == GOLDEN_HARVESTER_SINGLE_ID) {
                usesPerItem = 2;
            } else if (itemId == GOLDEN_HARVESTER_BUNDLE_ID) {
                usesPerItem = 4;
            } else if (itemId == FERTILIZER_SPREADER_ID) {
                usesPerItem = 3;
            }
            userToolUses[msg.sender][itemId] += usesPerItem * amount;
        }
        // Emit event (reuse ItemPurchased for consistency)
        uint256[] memory itemIds = new uint256[](1);
        uint256[] memory amounts = new uint256[](1);
        itemIds[0] = itemId;
        amounts[0] = amount;
        emit ItemPurchased(msg.sender, 0, itemIds, amounts, paymentToken, totalPrice);
        // Award XP for buying harvester, else 1 XP
        if (itemId == HARVESTER_ID || itemId == GOLDEN_HARVESTER_SINGLE_ID || itemId == GOLDEN_HARVESTER_BUNDLE_ID) {
            totalXP[msg.sender] += 5;
        } else {
            totalXP[msg.sender] += 1;
        }
    }

    // --- Planting & Harvesting ---
    function plantSeed(uint256 plotId, uint256 seedId) external whenNotPaused nonReentrant {
        require(userEnergy[msg.sender] >= 1, "Not enough energy");
        userEnergy[msg.sender] -= 1;
        Plot storage plot = userPlots[msg.sender][plotId];
        require(!plot.needsFertilizer, "Apply fertilizer first");
        require(userItemBalances[msg.sender][seedId] > 0, "No seeds");
        Item storage seed = items[seedId];
        require(seed.active && seed.itemType == 0, "Invalid seed");
        require(seed.growthBonusBP >= MIN_BONUS_BP && seed.growthBonusBP <= MAX_BONUS_BP, "Growth bonus out of range");
        require(seed.yieldBonusBP >= MIN_BONUS_BP && seed.yieldBonusBP <= MAX_BONUS_BP, "Yield bonus out of range");
        // Burn one use
        userItemBalances[msg.sender][seedId] -= 1;
        uint16 growthBonusBP = 0;
        uint16 yieldBonusBP = 0;
        if (userBundleSeeds[msg.sender][seedId] > 0) {
            userBundleSeeds[msg.sender][seedId] -= 1;
            // Apply correct bundle bonus from docs/marketplace
            if (seedId == 9) { growthBonusBP = 400; yieldBonusBP = 400; } // Basic Rice Seed: 40%
            else if (seedId == 10) { growthBonusBP = 200; yieldBonusBP = 200; } // Premium Rice Seed: 20%
            else if (seedId == 11) { growthBonusBP = 214; yieldBonusBP = 214; } // Hybrid Rice Seed: 21.43%
        } else if (userSingleSeeds[msg.sender][seedId] > 0) {
            userSingleSeeds[msg.sender][seedId] -= 1;
            // No bonus
            growthBonusBP = 0;
            yieldBonusBP = 0;
        } else {
            revert("No seeds available");
        }
        // Do not set plantedAt/readyAt/growing yet
        userPlots[msg.sender][plotId] = Plot({
            seedId: seedId,
            plantedAt: 0,
            readyAt: 0,
            growthBonusBP: growthBonusBP,
            yieldBonusBP: yieldBonusBP,
            growing: false,
            harvestedAt: 0,
            needsFertilizer: false,
            startedGrowing: false,
            state: PlotState.NeedsWater
        });
        emit SeedPlanted(msg.sender, plotId, seedId);
        totalXP[msg.sender] += 1; // Award 1 XP for planting
    }

    // --- Daily Rewards ---
    function setDailyRewardPaused(bool paused) external onlyOwner {
        dailyRewardPaused = paused;
    }

    function claimDailyReward() external whenNotPaused nonReentrant {
        require(!dailyRewardPaused, "Daily reward is paused");
        uint256 day = (userStreaks[msg.sender] % 7);
        require(block.timestamp > lastClaimedDay[msg.sender] + 1 days, "Already claimed today");
        uint256 reward = dailyRewards[day];
        riceTokens[msg.sender] += reward;
        userStreaks[msg.sender] += 1;
        lastClaimedDay[msg.sender] = block.timestamp;
        emit DailyRewardClaimed(msg.sender, day + 1, reward);
        totalXP[msg.sender] += 1; // Award 1 XP for claiming daily reward
    }

    // --- Tools/Upgrades Example ---
    function useTool(uint256 toolId, uint256 plotId) external whenNotPaused nonReentrant {
        require(userItemBalances[msg.sender][toolId] > 0, "No tool");
        Item storage tool = items[toolId];
        require(tool.active && tool.itemType == 1, "Invalid tool");
        userItemBalances[msg.sender][toolId] -= 1;
        // Apply tool effect (e.g., water instantly)
        emit ToolUsed(msg.sender, toolId, plotId);
    }
    function upgradeFarm(uint256 upgradeId) external whenNotPaused nonReentrant {
        require(userItemBalances[msg.sender][upgradeId] > 0, "No upgrade");
        Item storage upgrade = items[upgradeId];
        require(upgrade.active && upgrade.itemType == 2, "Invalid upgrade");
        userItemBalances[msg.sender][upgradeId] -= 1;
        farmLevel[msg.sender] += 1;
        emit FarmUpgraded(msg.sender, upgradeId);
        totalXP[msg.sender] += 1; // Award 1 XP for upgrading farm
    }

    // --- Quest Functions ---
    function setQuestReward(uint256 questId, uint256 reward) external onlyOwner {
        questRewards[questId] = reward;
    }

    function claimQuestReward(uint256 questId) external whenNotPaused nonReentrant {
        require(!claimedQuests[msg.sender][questId], "Quest already claimed");
        uint256 reward = questRewards[questId];
        require(reward > 0, "Invalid quest or no reward");
        
        // Add reward to user's RT balance
        riceTokens[msg.sender] += reward;
        claimedQuests[msg.sender][questId] = true;
        
        emit QuestRewardClaimed(msg.sender, questId, reward);
        totalXP[msg.sender] += 1; // Award 1 XP for claiming quest reward
    }

    // --- Utility ---
    function getUserInventory(address user, uint256[] calldata itemIds) external view returns (uint256[] memory) {
        uint256[] memory balances = new uint256[](itemIds.length);
        for (uint256 i = 0; i < itemIds.length; i++) {
            balances[i] = userItemBalances[user][itemIds[i]];
        }
        return balances;
    }
    function getUserPlots(address user, uint256[] calldata plotIds) external view returns (Plot[] memory) {
        Plot[] memory plots = new Plot[](plotIds.length);
        for (uint256 i = 0; i < plotIds.length; i++) {
            plots[i] = userPlots[user][plotIds[i]];
        }
        return plots;
    }
    function claimRiceTokens() external nonReentrant {
        uint256 amount = riceTokens[msg.sender];
        require(amount >= minClaimRT, "Below claim threshold");
        riceTokens[msg.sender] = 0;
        // Apply 3.69% burn on claim to reduce supply pressure
        uint256 burnBP = 369; // 3.69% in basis points (1/100 of percent)
        uint256 burnAmount = (amount * burnBP) / 10000;
        uint256 netAmount = amount - burnAmount;
        uint256 transferAmount = netAmount * 1e18; // Convert to ERC20 decimals
        uint256 burnTransfer = burnAmount * 1e18;
        require(riceToken.transfer(msg.sender, transferAmount), "Transfer failed");
        if (burnTransfer > 0) {
            require(riceToken.transfer(BURN_ADDRESS, burnTransfer), "Burn transfer failed");
        }
        emit RiceClaimed(msg.sender, netAmount);
    }

    /// Owner control: adjust the minimum RT threshold required for ERC20 claim
    function setMinClaimRT(uint256 newMin) external onlyOwner {
        minClaimRT = newMin;
    }
    // Fallback to receive ETH
    receive() external payable {}

    // --- New Function ---
    function waterCrop(uint256 plotId) external whenNotPaused nonReentrant {
        require(userEnergy[msg.sender] >= 1, "Not enough energy");
        userEnergy[msg.sender] -= 1;
        require(userItemBalances[msg.sender][WATERING_CAN_ID] > 0, "No watering can");
        userItemBalances[msg.sender][WATERING_CAN_ID] -= 1;
        Plot storage plot = userPlots[msg.sender][plotId];
        if (!plot.startedGrowing) {
            Item storage seed = items[plot.seedId];
            uint256 adjustedGrowthTime = seed.baseGrowthTime * (1000 - plot.growthBonusBP) / 1000;
            plot.plantedAt = block.timestamp;
            plot.readyAt = block.timestamp + adjustedGrowthTime;
            plot.growing = true;
            plot.startedGrowing = true;
            plot.state = PlotState.Growing;
        }
        // else: normal watering logic (e.g., increase water level, improve quality, etc.)
        emit ToolUsed(msg.sender, WATERING_CAN_ID, plotId);
        totalXP[msg.sender] += 1; // Award 1 XP for watering
    }

    // --- New Functions for Tools ---
    // 1. Single Golden Harvester: Harvest one plot with +20% bonus, burn one per use
    function harvestWithGoldenHarvester(uint256 plotId) external whenNotPaused nonReentrant {
        require(userEnergy[msg.sender] >= 1, "Not enough energy");
        userEnergy[msg.sender] -= 1;
        require(userItemBalances[msg.sender][GOLDEN_HARVESTER_SINGLE_ID] > 0, "Need Golden Harvester (Single)");
        Plot storage plot = userPlots[msg.sender][plotId];
        require(plot.growing, "Nothing growing");
        require(block.timestamp >= plot.readyAt, "Not ready");
        // Burn one harvester
        require(userToolUses[msg.sender][GOLDEN_HARVESTER_SINGLE_ID] > 0, "No harvester uses left");
        userToolUses[msg.sender][GOLDEN_HARVESTER_SINGLE_ID] -= 1;
        if (userToolUses[msg.sender][GOLDEN_HARVESTER_SINGLE_ID] == 0) {
            userItemBalances[msg.sender][GOLDEN_HARVESTER_SINGLE_ID] -= 1;
            emit ToolBurned(msg.sender, GOLDEN_HARVESTER_SINGLE_ID);
        }
        // Calculate yield with +20% bonus
        Item storage seed = items[plot.seedId];
        uint256 baseYield = seed.baseReward * (1000 + plot.yieldBonusBP) / 1000;
        uint256 bonusYield = baseYield * 20 / 100;
        uint256 totalYield = baseYield + bonusYield;
        riceTokens[msg.sender] += totalYield;
        plot.growing = false;
        plot.harvestedAt = block.timestamp;
        plot.needsFertilizer = true;
        plot.state = PlotState.Locked;
        emit Harvested(msg.sender, plotId, totalYield);
        totalHarvests[msg.sender] += 1;
        totalXP[msg.sender] += 1; // Award 1 XP for harvesting
    }

    // 2. Golden Harvester Bundle: Harvest all ready plots, burn one per use
    function multiHarvestWithGoldenHarvester() external whenNotPaused nonReentrant {
        require(userItemBalances[msg.sender][GOLDEN_HARVESTER_BUNDLE_ID] > 0, "Need Golden Harvester (Bundle)");
        uint256 totalYield = 0;
        uint256 plotsHarvested = 0;
        for (uint256 i = 0; i < 16; i++) {
            Plot storage plot = userPlots[msg.sender][i];
            if (plot.growing && block.timestamp >= plot.readyAt) {
                Item storage seed = items[plot.seedId];
                uint256 baseYield = seed.baseReward * (1000 + plot.yieldBonusBP) / 1000;
                uint256 bonusYield = baseYield * 20 / 100;
                uint256 plotYield = baseYield + bonusYield;
                totalYield += plotYield;
                plot.growing = false;
                plot.harvestedAt = block.timestamp;
                plot.needsFertilizer = true;
                plotsHarvested++;
                totalHarvests[msg.sender] += 1;
                emit Harvested(msg.sender, i, plotYield);
            }
        }
        require(plotsHarvested > 0, "No plots ready");
        // Burn one harvester bundle
        require(userToolUses[msg.sender][GOLDEN_HARVESTER_BUNDLE_ID] > 0, "No harvester bundle uses left");
        userToolUses[msg.sender][GOLDEN_HARVESTER_BUNDLE_ID] -= 1;
        if (userToolUses[msg.sender][GOLDEN_HARVESTER_BUNDLE_ID] == 0) {
            userItemBalances[msg.sender][GOLDEN_HARVESTER_BUNDLE_ID] -= 1;
            emit ToolBurned(msg.sender, GOLDEN_HARVESTER_BUNDLE_ID);
        }
        riceTokens[msg.sender] += totalYield;
        totalXP[msg.sender] += 1; // Award 1 XP for multi-harvest
    }

    // 3. Fertilizer Spreader: Apply to plot, boost growth/yield, burn one per use
    function applyFertilizer(uint256 plotId) external whenNotPaused nonReentrant {
        require(userItemBalances[msg.sender][FERTILIZER_SPREADER_ID] > 0, "No Fertilizer");
        Plot storage plot = userPlots[msg.sender][plotId];
        require(plot.growing, "Nothing growing");
        // Use fertilizer: 3 uses per item
        if (userToolUses[msg.sender][FERTILIZER_SPREADER_ID] == 0) {
            // Initialize uses for an existing fertilizer item if not set
            userToolUses[msg.sender][FERTILIZER_SPREADER_ID] = 3;
        }
        require(userToolUses[msg.sender][FERTILIZER_SPREADER_ID] > 0, "No fertilizer uses left");
        userToolUses[msg.sender][FERTILIZER_SPREADER_ID] -= 1;
        if (userToolUses[msg.sender][FERTILIZER_SPREADER_ID] == 0) {
            // Consumed one fertilizer item fully
            userItemBalances[msg.sender][FERTILIZER_SPREADER_ID] -= 1;
            emit ToolBurned(msg.sender, FERTILIZER_SPREADER_ID);
        }
        // Option B: Boost both growth and yield, and update readyAt
        uint256 elapsed = block.timestamp - plot.plantedAt;
        plot.growthBonusBP += 25;
        plot.yieldBonusBP += 15;
        Item storage seed = items[plot.seedId];
        uint256 newAdjustedGrowthTime = seed.baseGrowthTime * (1000 - plot.growthBonusBP) / 1000;
        if (elapsed < newAdjustedGrowthTime) {
            plot.readyAt = block.timestamp + (newAdjustedGrowthTime - elapsed);
        } else {
            plot.readyAt = block.timestamp; // Already ready!
        }
        emit ToolUsed(msg.sender, FERTILIZER_SPREADER_ID, plotId);
        totalXP[msg.sender] += 1; // Award 1 XP for applying fertilizer
    }

    function buyItemWithGameRT(uint256 itemId, uint256 amount) external whenNotPaused nonReentrant {
        Item storage item = items[itemId];
        require(item.active, "Item inactive");
        require(item.supply >= amount, "Insufficient supply");
        require(item.priceETH == 0, "Not an RT-priced item");
        // Dynamic RT pricing per item
        uint256 unitPriceRT = 50; // default 50 RT
        if (itemId == GOLDEN_HARVESTER_SINGLE_ID) {
            unitPriceRT = 25; // Single harvester priced at 25 RT
        } else if (itemId == FERTILIZER_SPREADER_ID) {
            unitPriceRT = 50; // Fertilizer remains 50 RT
        }
        uint256 priceRT = unitPriceRT * amount; // plain integer, no decimals
        require(riceTokens[msg.sender] >= priceRT, "Not enough RT");
        riceTokens[msg.sender] -= priceRT;
        item.supply -= amount;
        userItemBalances[msg.sender][itemId] += amount;
        // Assign uses for tools purchased via RT path
        if (item.itemType == 1) {
            uint256 usesPerItem = 5;
            if (itemId == GOLDEN_HARVESTER_SINGLE_ID) {
                usesPerItem = 2;
            } else if (itemId == GOLDEN_HARVESTER_BUNDLE_ID) {
                usesPerItem = 4;
            } else if (itemId == FERTILIZER_SPREADER_ID) {
                usesPerItem = 3;
            }
            userToolUses[msg.sender][itemId] += usesPerItem * amount;
        }
        emit ItemPurchasedWithRT(msg.sender, itemId, amount, priceRT);
        // Award XP for buying harvester, else 1 XP
        if (itemId == HARVESTER_ID || itemId == GOLDEN_HARVESTER_SINGLE_ID || itemId == GOLDEN_HARVESTER_BUNDLE_ID) {
            totalXP[msg.sender] += 5;
        } else {
            totalXP[msg.sender] += 1;
        }
    }



    function revivePlot(uint256 plotId) external whenNotPaused nonReentrant {
        Plot storage plot = userPlots[msg.sender][plotId];
        require(plot.needsFertilizer, "Plot does not need fertilizer");
        require(block.timestamp >= plot.harvestedAt + 1 weeks, "Cooldown not over"); // MAINNET: 1 week lock
        require(userItemBalances[msg.sender][FERTILIZER_SPREADER_ID] > 0, "No fertilizer");
        // Consume fertilizer using the 3-uses durability model (same as applyFertilizer)
        if (userToolUses[msg.sender][FERTILIZER_SPREADER_ID] == 0) {
            userToolUses[msg.sender][FERTILIZER_SPREADER_ID] = 3;
        }
        require(userToolUses[msg.sender][FERTILIZER_SPREADER_ID] > 0, "No fertilizer uses left");
        userToolUses[msg.sender][FERTILIZER_SPREADER_ID] -= 1;
        if (userToolUses[msg.sender][FERTILIZER_SPREADER_ID] == 0) {
            userItemBalances[msg.sender][FERTILIZER_SPREADER_ID] -= 1;
            emit ToolBurned(msg.sender, FERTILIZER_SPREADER_ID);
        }
        // Reset plot to empty state so it can be planted again
        plot.seedId = 0;
        plot.plantedAt = 0;
        plot.readyAt = 0;
        plot.growthBonusBP = 0;
        plot.yieldBonusBP = 0;
        plot.growing = false;
        plot.harvestedAt = 0;
        plot.needsFertilizer = false;
        plot.state = PlotState.Empty;
        totalXP[msg.sender] += 1; // Award 1 XP for reviving
    }

    // --- Claim Initial Energy (only once) ---
    function claimInitialEnergy() external whenNotPaused {
        require(!hasClaimedInitialEnergy[msg.sender], "Already claimed");
        userEnergy[msg.sender] += 10;
        hasClaimedInitialEnergy[msg.sender] = true;
    }

    // --- Buy Energy Booster (25 RT for 25 energy) ---
    function buyEnergyBooster() external whenNotPaused nonReentrant {
        uint256 boosterCost = 25; // 25 RT per booster
        uint256 energyAmount = 25; // Each booster gives 25 energy
        require(riceTokens[msg.sender] >= boosterCost, "Not enough RT");
        require(items[ENERGY_BOOSTER_ID].supply > 0, "Energy Booster sold out");
        riceTokens[msg.sender] -= boosterCost;
        userEnergy[msg.sender] += energyAmount;
        items[ENERGY_BOOSTER_ID].supply -= 1;
        // Optionally emit an event
    }

    function getEnergyBoosterSupply() external view returns (uint256) {
        return items[ENERGY_BOOSTER_ID].supply;
    }

    // --- Admin Functions ---
    // Give energy to a user (max 3 energy per call for safety)
    function giveEnergyToUser(address user, uint256 energyAmount) external onlyOwner whenNotPaused {
        require(user != address(0), "Invalid user address");
        require(energyAmount > 0 && energyAmount <= 3, "Energy amount must be 1-3");
        userEnergy[user] += energyAmount;
        // Emit event for transparency
        emit EnergyGiven(user, energyAmount, block.timestamp);
    }

    // --- Secondary Marketplace (internal) ---
    // List seeds for sale (ETH-denominated). Escrows items by decrementing seller balance.
    function listItem(uint256 itemId, uint256 amount, uint256 priceETH) external whenNotPaused nonReentrant returns (uint256 listingId) {
        require(amount > 0, "Amount=0");
        require(priceETH > 0, "Price=0");
        Item storage item = items[itemId];
        require(item.active, "Item inactive");
        require(item.itemType == 0, "Only seeds");
        require(userItemBalances[msg.sender][itemId] >= amount, "Insufficient balance");
        
        // Escrow: decrement seller balance and track origin
        userItemBalances[msg.sender][itemId] -= amount;
        
        // For seeds, try to decrement from single seeds first, then bundle seeds
        if (item.itemType == 0) { // If it's a seed
            if (userSingleSeeds[msg.sender][itemId] >= amount) {
                userSingleSeeds[msg.sender][itemId] -= amount;
            } else if (userBundleSeeds[msg.sender][itemId] >= amount) {
                userBundleSeeds[msg.sender][itemId] -= amount;
            } else {
                // If neither has enough, decrement proportionally
                uint256 fromSingles = userSingleSeeds[msg.sender][itemId];
                uint256 fromBundles = userBundleSeeds[msg.sender][itemId];
                
                if (fromSingles > 0) {
                    userSingleSeeds[msg.sender][itemId] = 0;
                    amount -= fromSingles;
                }
                
                if (amount > 0 && fromBundles > 0) {
                    userBundleSeeds[msg.sender][itemId] -= amount;
                }
            }
        }
        
        listingId = ++nextListingId;
        listings[listingId] = Listing({
            id: listingId,
            seller: msg.sender,
            itemId: itemId,
            amount: amount,
            priceETH: priceETH,
            active: true
        });
        emit Listed(listingId, msg.sender, itemId, amount, priceETH);
    }

    // Cancel an active listing and return remaining items to the seller.
    function cancelListing(uint256 listingId) external nonReentrant {
        Listing storage l = listings[listingId];
        require(l.active, "Not active");
        require(l.seller == msg.sender, "Not seller");
        uint256 remaining = l.amount;
        l.active = false;
        l.amount = 0;
        if (remaining > 0) {
            userItemBalances[msg.sender][l.itemId] += remaining;
            
            // For seeds, restore to single seeds (since they were sold individually)
            Item storage item = items[l.itemId];
            if (item.itemType == 0) { // If it's a seed
                userSingleSeeds[msg.sender][l.itemId] += remaining;
            }
        }
        emit Cancelled(listingId);
    }

    // Buy some or all of a listing. Accepts ETH; refunds excess.
    function buyListing(uint256 listingId, uint256 amountToBuy) external payable whenNotPaused nonReentrant {
        require(amountToBuy > 0, "Amount=0");
        Listing storage l = listings[listingId];
        require(l.active, "Not active");
        require(l.amount >= amountToBuy, "Insufficient listed");
        uint256 totalPrice = l.priceETH * amountToBuy;
        require(msg.value >= totalPrice, "Insufficient ETH");
        // Compute fee and proceeds
        uint256 fee = (totalPrice * marketplaceFeeBP) / 10000;
        uint256 proceeds = totalPrice - fee;
        // Update listing first (checks-effects-interactions)
        l.amount -= amountToBuy;
        if (l.amount == 0) {
            l.active = false;
        }
        // Credit buyer
        userItemBalances[msg.sender][l.itemId] += amountToBuy;
        
        // Track seeds as single seeds since they're being sold individually
        Item storage item = items[l.itemId];
        if (item.itemType == 0) { // If it's a seed
            userSingleSeeds[msg.sender][l.itemId] += amountToBuy;
        }
        // Payouts
        if (fee > 0 && treasury != address(0)) {
            (bool fs, ) = payable(treasury).call{value: fee}("");
            require(fs, "Fee transfer failed");
        }
        (bool ss, ) = payable(l.seller).call{value: proceeds}("");
        require(ss, "Proceeds transfer failed");
        // Refund any excess
        if (msg.value > totalPrice) {
            (bool rs, ) = payable(msg.sender).call{value: msg.value - totalPrice}("");
            require(rs, "Refund failed");
        }
        emit Bought(listingId, msg.sender, amountToBuy, totalPrice, fee, proceeds);
    }

    // Admin: adjust fee and treasury
    function setMarketplaceFeeBP(uint16 feeBP) external onlyOwner {
        require(feeBP <= 1000, "Fee too high"); // max 10%
        marketplaceFeeBP = feeBP;
    }
    function setTreasury(address treasury_) external onlyOwner {
        treasury = treasury_;
    }
    
    // Helper function to get user's seed breakdown
    function getUserSeedBreakdown(address user, uint256 seedId) external view returns (uint256 total, uint256 singles, uint256 bundleSeeds) {
        total = userItemBalances[user][seedId];
        singles = userSingleSeeds[user][seedId];
        bundleSeeds = userBundleSeeds[user][seedId];
    }
}

Other files

  • @openzeppelin/contracts/access/Ownable.sol
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
    
    pragma solidity ^0.8.20;
    
    import {Context} from "../utils/Context.sol";
    
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * The initial owner is set to the address provided by the deployer. This can
     * later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract Ownable is Context {
        address private _owner;
    
        /**
         * @dev The caller account is not authorized to perform an operation.
         */
        error OwnableUnauthorizedAccount(address account);
    
        /**
         * @dev The owner is not a valid owner account. (eg. `address(0)`)
         */
        error OwnableInvalidOwner(address owner);
    
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    
        /**
         * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
         */
        constructor(address initialOwner) {
            if (initialOwner == address(0)) {
                revert OwnableInvalidOwner(address(0));
            }
            _transferOwnership(initialOwner);
        }
    
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            _checkOwner();
            _;
        }
    
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
    
        /**
         * @dev Throws if the sender is not the owner.
         */
        function _checkOwner() internal view virtual {
            if (owner() != _msgSender()) {
                revert OwnableUnauthorizedAccount(_msgSender());
            }
        }
    
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby disabling any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            _transferOwnership(address(0));
        }
    
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual onlyOwner {
            if (newOwner == address(0)) {
                revert OwnableInvalidOwner(address(0));
            }
            _transferOwnership(newOwner);
        }
    
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    
  • @openzeppelin/contracts/token/ERC20/IERC20.sol
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
    
    pragma solidity ^0.8.20;
    
    /**
     * @dev Interface of the ERC-20 standard as defined in the ERC.
     */
    interface IERC20 {
        /**
         * @dev Emitted when `value` tokens are moved from one account (`from`) to
         * another (`to`).
         *
         * Note that `value` may be zero.
         */
        event Transfer(address indexed from, address indexed to, uint256 value);
    
        /**
         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
         * a call to {approve}. `value` is the new allowance.
         */
        event Approval(address indexed owner, address indexed spender, uint256 value);
    
        /**
         * @dev Returns the value of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
    
        /**
         * @dev Returns the value of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
    
        /**
         * @dev Moves a `value` amount of tokens from the caller's account to `to`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address to, uint256 value) external returns (bool);
    
        /**
         * @dev Returns the remaining number of tokens that `spender` will be
         * allowed to spend on behalf of `owner` through {transferFrom}. This is
         * zero by default.
         *
         * This value changes when {approve} or {transferFrom} are called.
         */
        function allowance(address owner, address spender) external view returns (uint256);
    
        /**
         * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
         * caller's tokens.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * IMPORTANT: Beware that changing an allowance with this method brings the risk
         * that someone may use both the old and the new allowance by unfortunate
         * transaction ordering. One possible solution to mitigate this race
         * condition is to first reduce the spender's allowance to 0 and set the
         * desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         *
         * Emits an {Approval} event.
         */
        function approve(address spender, uint256 value) external returns (bool);
    
        /**
         * @dev Moves a `value` amount of tokens from `from` to `to` using the
         * allowance mechanism. `value` is then deducted from the caller's
         * allowance.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(address from, address to, uint256 value) external returns (bool);
    }
    
  • @openzeppelin/contracts/utils/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;
        }
    }
    
  • @openzeppelin/contracts/utils/Pausable.sol
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.3.0) (utils/Pausable.sol)
    
    pragma solidity ^0.8.20;
    
    import {Context} from "../utils/Context.sol";
    
    /**
     * @dev Contract module which allows children to implement an emergency stop
     * mechanism that can be triggered by an authorized account.
     *
     * This module is used through inheritance. It will make available the
     * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
     * the functions of your contract. Note that they will not be pausable by
     * simply including this module, only once the modifiers are put in place.
     */
    abstract contract Pausable is Context {
        bool private _paused;
    
        /**
         * @dev Emitted when the pause is triggered by `account`.
         */
        event Paused(address account);
    
        /**
         * @dev Emitted when the pause is lifted by `account`.
         */
        event Unpaused(address account);
    
        /**
         * @dev The operation failed because the contract is paused.
         */
        error EnforcedPause();
    
        /**
         * @dev The operation failed because the contract is not paused.
         */
        error ExpectedPause();
    
        /**
         * @dev Modifier to make a function callable only when the contract is not paused.
         *
         * Requirements:
         *
         * - The contract must not be paused.
         */
        modifier whenNotPaused() {
            _requireNotPaused();
            _;
        }
    
        /**
         * @dev Modifier to make a function callable only when the contract is paused.
         *
         * Requirements:
         *
         * - The contract must be paused.
         */
        modifier whenPaused() {
            _requirePaused();
            _;
        }
    
        /**
         * @dev Returns true if the contract is paused, and false otherwise.
         */
        function paused() public view virtual returns (bool) {
            return _paused;
        }
    
        /**
         * @dev Throws if the contract is paused.
         */
        function _requireNotPaused() internal view virtual {
            if (paused()) {
                revert EnforcedPause();
            }
        }
    
        /**
         * @dev Throws if the contract is not paused.
         */
        function _requirePaused() internal view virtual {
            if (!paused()) {
                revert ExpectedPause();
            }
        }
    
        /**
         * @dev Triggers stopped state.
         *
         * Requirements:
         *
         * - The contract must not be paused.
         */
        function _pause() internal virtual whenNotPaused {
            _paused = true;
            emit Paused(_msgSender());
        }
    
        /**
         * @dev Returns to normal state.
         *
         * Requirements:
         *
         * - The contract must be paused.
         */
        function _unpause() internal virtual whenPaused {
            _paused = false;
            emit Unpaused(_msgSender());
        }
    }
    
  • @openzeppelin/contracts/utils/ReentrancyGuard.sol
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)
    
    pragma solidity ^0.8.20;
    
    /**
     * @dev Contract module that helps prevent reentrant calls to a function.
     *
     * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
     * available, which can be applied to functions to make sure there are no nested
     * (reentrant) calls to them.
     *
     * Note that because there is a single `nonReentrant` guard, functions marked as
     * `nonReentrant` may not call one another. This can be worked around by making
     * those functions `private`, and then adding `external` `nonReentrant` entry
     * points to them.
     *
     * TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
     * consider using {ReentrancyGuardTransient} instead.
     *
     * TIP: If you would like to learn more about reentrancy and alternative ways
     * to protect against it, check out our blog post
     * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
     */
    abstract contract ReentrancyGuard {
        // Booleans are more expensive than uint256 or any type that takes up a full
        // word because each write operation emits an extra SLOAD to first read the
        // slot's contents, replace the bits taken up by the boolean, and then write
        // back. This is the compiler's defense against contract upgrades and
        // pointer aliasing, and it cannot be disabled.
    
        // The values being non-zero value makes deployment a bit more expensive,
        // but in exchange the refund on every call to nonReentrant will be lower in
        // amount. Since refunds are capped to a percentage of the total
        // transaction's gas, it is best to keep them low in cases like this one, to
        // increase the likelihood of the full refund coming into effect.
        uint256 private constant NOT_ENTERED = 1;
        uint256 private constant ENTERED = 2;
    
        uint256 private _status;
    
        /**
         * @dev Unauthorized reentrant call.
         */
        error ReentrancyGuardReentrantCall();
    
        constructor() {
            _status = NOT_ENTERED;
        }
    
        /**
         * @dev Prevents a contract from calling itself, directly or indirectly.
         * Calling a `nonReentrant` function from another `nonReentrant`
         * function is not supported. It is possible to prevent this from happening
         * by making the `nonReentrant` function external, and making it call a
         * `private` function that does the actual work.
         */
        modifier nonReentrant() {
            _nonReentrantBefore();
            _;
            _nonReentrantAfter();
        }
    
        function _nonReentrantBefore() private {
            // On the first call to nonReentrant, _status will be NOT_ENTERED
            if (_status == ENTERED) {
                revert ReentrancyGuardReentrantCall();
            }
    
            // Any calls to nonReentrant after this point will fail
            _status = ENTERED;
        }
    
        function _nonReentrantAfter() private {
            // By storing the original value once again, a refund is triggered (see
            // https://eips.ethereum.org/EIPS/eip-2200)
            _status = NOT_ENTERED;
        }
    
        /**
         * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
         * `nonReentrant` function in the call stack.
         */
        function _reentrancyGuardEntered() internal view returns (bool) {
            return _status == ENTERED;
        }
    }
    
ABI · 116 entries
json
[
  {
    "type": "constructor",
    "inputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "EnforcedPause",
    "type": "error",
    "inputs": []
  },
  {
    "name": "ExpectedPause",
    "type": "error",
    "inputs": []
  },
  {
    "name": "OwnableInvalidOwner",
    "type": "error",
    "inputs": [
      {
        "name": "owner",
        "type": "address",
        "internalType": "address"
      }
    ]
  },
  {
    "name": "OwnableUnauthorizedAccount",
    "type": "error",
    "inputs": [
      {
        "name": "account",
        "type": "address",
        "internalType": "address"
      }
    ]
  },
  {
    "name": "ReentrancyGuardReentrantCall",
    "type": "error",
    "inputs": []
  },
  {
    "name": "Bought",
    "type": "event",
    "inputs": [
      {
        "name": "listingId",
        "type": "uint256",
        "indexed": true,
        "internalType": "uint256"
      },
      {
        "name": "buyer",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "amount",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "totalPaid",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "fee",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "sellerProceeds",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "BundlePurchased",
    "type": "event",
    "inputs": [
      {
        "name": "user",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "bundleId",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "itemIds",
        "type": "uint256[]",
        "indexed": false,
        "internalType": "uint256[]"
      },
      {
        "name": "amounts",
        "type": "uint256[]",
        "indexed": false,
        "internalType": "uint256[]"
      },
      {
        "name": "price",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "Cancelled",
    "type": "event",
    "inputs": [
      {
        "name": "listingId",
        "type": "uint256",
        "indexed": true,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "DailyRewardClaimed",
    "type": "event",
    "inputs": [
      {
        "name": "user",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "day",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "reward",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "EnergyGiven",
    "type": "event",
    "inputs": [
      {
        "name": "user",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "energyAmount",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "timestamp",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "FarmUpgraded",
    "type": "event",
    "inputs": [
      {
        "name": "user",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "upgradeId",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "Harvested",
    "type": "event",
    "inputs": [
      {
        "name": "user",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "plotId",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "reward",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "ItemPurchased",
    "type": "event",
    "inputs": [
      {
        "name": "user",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "bundleId",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "itemIds",
        "type": "uint256[]",
        "indexed": false,
        "internalType": "uint256[]"
      },
      {
        "name": "amounts",
        "type": "uint256[]",
        "indexed": false,
        "internalType": "uint256[]"
      },
      {
        "name": "paymentToken",
        "type": "address",
        "indexed": false,
        "internalType": "address"
      },
      {
        "name": "price",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "ItemPurchasedWithRT",
    "type": "event",
    "inputs": [
      {
        "name": "user",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "itemId",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "amount",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "totalRT",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "Listed",
    "type": "event",
    "inputs": [
      {
        "name": "listingId",
        "type": "uint256",
        "indexed": true,
        "internalType": "uint256"
      },
      {
        "name": "seller",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "itemId",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "amount",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "priceETH",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "OwnershipTransferred",
    "type": "event",
    "inputs": [
      {
        "name": "previousOwner",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "newOwner",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      }
    ],
    "anonymous": false
  },
  {
    "name": "Paused",
    "type": "event",
    "inputs": [
      {
        "name": "account",
        "type": "address",
        "indexed": false,
        "internalType": "address"
      }
    ],
    "anonymous": false
  },
  {
    "name": "PaymentTokenAdded",
    "type": "event",
    "inputs": [
      {
        "name": "token",
        "type": "address",
        "indexed": false,
        "internalType": "address"
      }
    ],
    "anonymous": false
  },
  {
    "name": "PaymentTokenRemoved",
    "type": "event",
    "inputs": [
      {
        "name": "token",
        "type": "address",
        "indexed": false,
        "internalType": "address"
      }
    ],
    "anonymous": false
  },
  {
    "name": "PlotReady",
    "type": "event",
    "inputs": [
      {
        "name": "user",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "plotId",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "QuestRewardClaimed",
    "type": "event",
    "inputs": [
      {
        "name": "user",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "questId",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "reward",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "RiceClaimed",
    "type": "event",
    "inputs": [
      {
        "name": "user",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "amount",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "SeedPlanted",
    "type": "event",
    "inputs": [
      {
        "name": "user",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "plotId",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "seedId",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "ToolBurned",
    "type": "event",
    "inputs": [
      {
        "name": "user",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "toolId",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "ToolUsed",
    "type": "event",
    "inputs": [
      {
        "name": "user",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "toolId",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "plotId",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "Unpaused",
    "type": "event",
    "inputs": [
      {
        "name": "account",
        "type": "address",
        "indexed": false,
        "internalType": "address"
      }
    ],
    "anonymous": false
  },
  {
    "name": "BASIC_SEED_SINGLE_ID",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "BURN_ADDRESS",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "ENERGY_BOOSTER_ID",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "FERTILIZER_SPREADER_ID",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "GOLDEN_HARVESTER_BUNDLE_ID",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "GOLDEN_HARVESTER_SINGLE_ID",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "HARVESTER_ID",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "HYBRID_SEED_SINGLE_ID",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "MAX_BONUS_BP",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint16",
        "internalType": "uint16"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "MIN_BONUS_BP",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint16",
        "internalType": "uint16"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "PREMIUM_SEED_SINGLE_ID",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "STARTER_BUNDLE_ID",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "WATERING_CAN_ID",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "acceptedPaymentTokens",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bool",
        "internalType": "bool"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "addBundle",
    "type": "function",
    "inputs": [
      {
        "name": "bundle",
        "type": "tuple",
        "components": [
          {
            "name": "id",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "name",
            "type": "string",
            "internalType": "string"
          },
          {
            "name": "itemIds",
            "type": "uint256[]",
            "internalType": "uint256[]"
          },
          {
            "name": "itemAmounts",
            "type": "uint256[]",
            "internalType": "uint256[]"
          },
          {
            "name": "priceETH",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "paymentToken",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "active",
            "type": "bool",
            "internalType": "bool"
          },
          {
            "name": "maxSupply",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "supply",
            "type": "uint256",
            "internalType": "uint256"
          }
        ],
        "internalType": "struct SomniaFarming.Bundle"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "addItem",
    "type": "function",
    "inputs": [
      {
        "name": "item",
        "type": "tuple",
        "components": [
          {
            "name": "id",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "name",
            "type": "string",
            "internalType": "string"
          },
          {
            "name": "itemType",
            "type": "uint8",
            "internalType": "uint8"
          },
          {
            "name": "priceETH",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "paymentToken",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "baseReward",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "baseGrowthTime",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "growthBonusBP",
            "type": "uint16",
            "internalType": "uint16"
          },
          {
            "name": "yieldBonusBP",
            "type": "uint16",
            "internalType": "uint16"
          },
          {
            "name": "active",
            "type": "bool",
            "internalType": "bool"
          },
          {
            "name": "maxSupply",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "supply",
            "type": "uint256",
            "internalType": "uint256"
          }
        ],
        "internalType": "struct SomniaFarming.Item"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "addPaymentToken",
    "type": "function",
    "inputs": [
      {
        "name": "token",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "applyFertilizer",
    "type": "function",
    "inputs": [
      {
        "name": "plotId",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "bundles",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "id",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "name",
        "type": "string",
        "internalType": "string"
      },
      {
        "name": "priceETH",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "paymentToken",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "active",
        "type": "bool",
        "internalType": "bool"
      },
      {
        "name": "maxSupply",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "supply",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "buyBundle",
    "type": "function",
    "inputs": [
      {
        "name": "bundleId",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "amount",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "paymentToken",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [],
    "stateMutability": "payable"
  },
  {
    "name": "buyEnergyBooster",
    "type": "function",
    "inputs": [],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "buyItem",
    "type": "function",
    "inputs": [
      {
        "name": "itemId",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "amount",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "paymentToken",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [],
    "stateMutability": "payable"
  },
  {
    "name": "buyItemWithGameRT",
    "type": "function",
    "inputs": [
      {
        "name": "itemId",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "amount",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "buyListing",
    "type": "function",
    "inputs": [
      {
        "name": "listingId",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "amountToBuy",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "payable"
  },
  {
    "name": "cancelListing",
    "type": "function",
    "inputs": [
      {
        "name": "listingId",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "claimDailyReward",
    "type": "function",
    "inputs": [],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "claimInitialEnergy",
    "type": "function",
    "inputs": [],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "claimQuestReward",
    "type": "function",
    "inputs": [
      {
        "name": "questId",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "claimRiceTokens",
    "type": "function",
    "inputs": [],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "claimedQuests",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bool",
        "internalType": "bool"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "dailyRewardPaused",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "bool",
        "internalType": "bool"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "dailyRewards",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "deactivateBundle",
    "type": "function",
    "inputs": [
      {
        "name": "bundleId",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "deactivateItem",
    "type": "function",
    "inputs": [
      {
        "name": "itemId",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "farmLevel",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "getEnergyBoosterSupply",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "getUserInventory",
    "type": "function",
    "inputs": [
      {
        "name": "user",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "itemIds",
        "type": "uint256[]",
        "internalType": "uint256[]"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint256[]",
        "internalType": "uint256[]"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "getUserPlots",
    "type": "function",
    "inputs": [
      {
        "name": "user",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "plotIds",
        "type": "uint256[]",
        "internalType": "uint256[]"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "tuple[]",
        "components": [
          {
            "name": "seedId",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "plantedAt",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "readyAt",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "growthBonusBP",
            "type": "uint16",
            "internalType": "uint16"
          },
          {
            "name": "yieldBonusBP",
            "type": "uint16",
            "internalType": "uint16"
          },
          {
            "name": "growing",
            "type": "bool",
            "internalType": "bool"
          },
          {
            "name": "harvestedAt",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "needsFertilizer",
            "type": "bool",
            "internalType": "bool"
          },
          {
            "name": "startedGrowing",
            "type": "bool",
            "internalType": "bool"
          },
          {
            "name": "state",
            "type": "uint8",
            "internalType": "enum SomniaFarming.PlotState"
          }
        ],
        "internalType": "struct SomniaFarming.Plot[]"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "getUserSeedBreakdown",
    "type": "function",
    "inputs": [
      {
        "name": "user",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "seedId",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "total",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "singles",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "bundleSeeds",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "giveEnergyToUser",
    "type": "function",
    "inputs": [
      {
        "name": "user",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "energyAmount",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "harvestWithGoldenHarvester",
    "type": "function",
    "inputs": [
      {
        "name": "plotId",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "hasAutoWatering",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bool",
        "internalType": "bool"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "hasClaimedInitialEnergy",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bool",
        "internalType": "bool"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "items",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "id",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "name",
        "type": "string",
        "internalType": "string"
      },
      {
        "name": "itemType",
        "type": "uint8",
        "internalType": "uint8"
      },
      {
        "name": "priceETH",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "paymentToken",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "baseReward",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "baseGrowthTime",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "growthBonusBP",
        "type": "uint16",
        "internalType": "uint16"
      },
      {
        "name": "yieldBonusBP",
        "type": "uint16",
        "internalType": "uint16"
      },
      {
        "name": "active",
        "type": "bool",
        "internalType": "bool"
      },
      {
        "name": "maxSupply",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "supply",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "lastClaimedDay",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "listItem",
    "type": "function",
    "inputs": [
      {
        "name": "itemId",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "amount",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "priceETH",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "listingId",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "nonpayable"
  },
  {
    "name": "listings",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "id",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "seller",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "itemId",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "amount",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "priceETH",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "active",
        "type": "bool",
        "internalType": "bool"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "marketplaceFeeBP",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint16",
        "internalType": "uint16"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "minClaimRT",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "multiHarvestWithGoldenHarvester",
    "type": "function",
    "inputs": [],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "nextListingId",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "owner",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "pause",
    "type": "function",
    "inputs": [],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "paused",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "bool",
        "internalType": "bool"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "plantSeed",
    "type": "function",
    "inputs": [
      {
        "name": "plotId",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "seedId",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "playerLevel",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "questRewards",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "removePaymentToken",
    "type": "function",
    "inputs": [
      {
        "name": "token",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "renounceOwnership",
    "type": "function",
    "inputs": [],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "revivePlot",
    "type": "function",
    "inputs": [
      {
        "name": "plotId",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "riceToken",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "contract IERC20"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "riceTokens",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "setDailyRewardPaused",
    "type": "function",
    "inputs": [
      {
        "name": "paused",
        "type": "bool",
        "internalType": "bool"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "setItemMaxSupply",
    "type": "function",
    "inputs": [
      {
        "name": "itemId",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "maxSupply",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "setMarketplaceFeeBP",
    "type": "function",
    "inputs": [
      {
        "name": "feeBP",
        "type": "uint16",
        "internalType": "uint16"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "setMinClaimRT",
    "type": "function",
    "inputs": [
      {
        "name": "newMin",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "setQuestReward",
    "type": "function",
    "inputs": [
      {
        "name": "questId",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "reward",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "setRiceToken",
    "type": "function",
    "inputs": [
      {
        "name": "_riceToken",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "setTreasury",
    "type": "function",
    "inputs": [
      {
        "name": "treasury_",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "totalHarvests",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "totalXP",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "transferOwnership",
    "type": "function",
    "inputs": [
      {
        "name": "newOwner",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "treasury",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "unpause",
    "type": "function",
    "inputs": [],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "updateBundle",
    "type": "function",
    "inputs": [
      {
        "name": "bundle",
        "type": "tuple",
        "components": [
          {
            "name": "id",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "name",
            "type": "string",
            "internalType": "string"
          },
          {
            "name": "itemIds",
            "type": "uint256[]",
            "internalType": "uint256[]"
          },
          {
            "name": "itemAmounts",
            "type": "uint256[]",
            "internalType": "uint256[]"
          },
          {
            "name": "priceETH",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "paymentToken",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "active",
            "type": "bool",
            "internalType": "bool"
          },
          {
            "name": "maxSupply",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "supply",
            "type": "uint256",
            "internalType": "uint256"
          }
        ],
        "internalType": "struct SomniaFarming.Bundle"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "updateItem",
    "type": "function",
    "inputs": [
      {
        "name": "item",
        "type": "tuple",
        "components": [
          {
            "name": "id",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "name",
            "type": "string",
            "internalType": "string"
          },
          {
            "name": "itemType",
            "type": "uint8",
            "internalType": "uint8"
          },
          {
            "name": "priceETH",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "paymentToken",
            "type": "address",
            "internalType": "address"
          },
          {
            "name": "baseReward",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "baseGrowthTime",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "growthBonusBP",
            "type": "uint16",
            "internalType": "uint16"
          },
          {
            "name": "yieldBonusBP",
            "type": "uint16",
            "internalType": "uint16"
          },
          {
            "name": "active",
            "type": "bool",
            "internalType": "bool"
          },
          {
            "name": "maxSupply",
            "type": "uint256",
            "internalType": "uint256"
          },
          {
            "name": "supply",
            "type": "uint256",
            "internalType": "uint256"
          }
        ],
        "internalType": "struct SomniaFarming.Item"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "upgradeFarm",
    "type": "function",
    "inputs": [
      {
        "name": "upgradeId",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "useTool",
    "type": "function",
    "inputs": [
      {
        "name": "toolId",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "plotId",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "userBundleSeeds",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "userEnergy",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "userItemBalances",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "userPlots",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "seedId",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "plantedAt",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "readyAt",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "growthBonusBP",
        "type": "uint16",
        "internalType": "uint16"
      },
      {
        "name": "yieldBonusBP",
        "type": "uint16",
        "internalType": "uint16"
      },
      {
        "name": "growing",
        "type": "bool",
        "internalType": "bool"
      },
      {
        "name": "harvestedAt",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "needsFertilizer",
        "type": "bool",
        "internalType": "bool"
      },
      {
        "name": "startedGrowing",
        "type": "bool",
        "internalType": "bool"
      },
      {
        "name": "state",
        "type": "uint8",
        "internalType": "enum SomniaFarming.PlotState"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "userSingleSeeds",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "userStreaks",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "userToolUses",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "waterCrop",
    "type": "function",
    "inputs": [
      {
        "name": "plotId",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "withdrawETH",
    "type": "function",
    "inputs": [],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "withdrawToken",
    "type": "function",
    "inputs": [
      {
        "name": "token",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "type": "receive",
    "stateMutability": "payable"
  }
]