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"
}
]