Compare commits

..

No commits in common. "a7c03a2703408a2f0a50ea8430798ad46c5fcc73" and "b9390c18ab59b9e196fed4bf260bb4fb34d3c8e5" have entirely different histories.

19 changed files with 447 additions and 499 deletions

View File

@ -6,11 +6,8 @@ COPY package*.json ./
RUN npm install RUN npm install
# RUN npm run build
COPY . . COPY . .
EXPOSE 3000 EXPOSE 3000
# CMD ["npm", "run", "start"]
CMD ["npm", "run", "dev"] CMD ["npm", "run", "dev"]

View File

@ -1,4 +1,4 @@
# Moon Miners #Moon Miners
## Getting Started ## Getting Started
@ -26,5 +26,5 @@ DB_DATABASE=
## API Endpoints ## API Endpoints
Refer to `test-endpoints.restclient` for an up-to-date list of endpoints. If you want to use this to Refer to ~test-endpoints.restclient~ for an up-to-date list of endpoints. If you want to use this to
actually query the endpoints, it's not too hard to setup with Emacs. actually query the endpoints, it's not too hard to setup with Emacs.

View File

@ -6,19 +6,19 @@ begin
name text not null, name text not null,
description text not null, description text not null,
image text not null, image text not null,
claimAmount float8 not null, claimAmount integer not null,
completionTimeInMins integer not null, completionTimeInMins integer not null,
basePrice float8 not null, basePrice integer not null,
tier_1_price float8 not null, tier_1_price integer not null,
tier_1_claimboost float8 not null, tier_1_claimboost integer not null,
tier_2_price float8 not null, tier_2_price integer not null,
tier_2_claimboost float8 not null, tier_2_claimboost integer not null,
tier_3_price float8 not null, tier_3_price integer not null,
tier_3_claimboost float8 not null, tier_3_claimboost integer not null,
tier_4_price float8 not null, tier_4_price integer not null,
tier_4_claimboost float8 not null, tier_4_claimboost integer not null,
tier_5_price float8 not null, tier_5_price integer not null,
tier_5_claimboost float8 not null tier_5_claimboost integer not null
) on commit drop; ) on commit drop;
create temporary table if not exists stakingSources( create temporary table if not exists stakingSources(
id integer primary key, id integer primary key,
@ -130,12 +130,6 @@ begin
end; end;
$$ language plpgsql; $$ language plpgsql;
-- Needed to load the python3 extension
create or replace trusted language plpython3u;
-- If you don't grant this permission then devs will not be able to update this function
-- It's dangerous though, I guess
update pg_language set lanpltrusted = true where lanname = 'plpython3u';
create or replace function download_sheets() create or replace function download_sheets()
returns void as $$ returns void as $$
import requests import requests

View File

@ -8,7 +8,6 @@ as $$
declare declare
new_uuid uuid; new_uuid uuid;
res_id integer; res_id integer;
currency_start_amount float8;
begin begin
if p_user_id is null then if p_user_id is null then
insert into users(name, wallet) values (p_name, p_wallet) insert into users(name, wallet) values (p_name, p_wallet)
@ -18,11 +17,7 @@ begin
insert into users(id, name, wallet) values (p_user_id, p_name, p_wallet); insert into users(id, name, wallet) values (p_user_id, p_name, p_wallet);
end if; end if;
select value into currency_start_amount insert into bank_account(user_id, balance) values (new_uuid, 500);
from game_constants
where key = 'MoonbucksStartAmount';
insert into bank_account(user_id, balance) values (new_uuid, currency_start_amount);
insert into resource_account (resource_id, user_id, balance) insert into resource_account (resource_id, user_id, balance)
select resource.id, new_uuid, 50 select resource.id, new_uuid, 50
@ -36,7 +31,7 @@ $$ language plpgsql;
create or replace function get_accounts(user_id uuid) create or replace function get_accounts(user_id uuid)
returns table ( returns table (
id uuid, id uuid,
"primaryBalance" float8, "primaryBalance" integer,
"resourceAccounts" json "resourceAccounts" json
) )
as $$ as $$
@ -66,23 +61,23 @@ create or replace function sell_resources(
) )
returns table ( returns table (
"resourceName" text, "resourceName" text,
"resourceAmount" float8, "resourceAmount" integer,
"returnAmount" float8, "returnAmount" integer,
"saleResult" text "saleResult" text
) )
as $$ as $$
declare declare
resource jsonb; resource jsonb;
resource_name text; resource_name text;
resource_amount float8; resource_amount integer;
return_amount float8; return_amount integer;
resource_sell_factor real; resource_sell_factor real;
sale_result text; sale_result text;
begin begin
for resource in select * from jsonb_array_elements(p_resources) for resource in select * from jsonb_array_elements(p_resources)
loop loop
resource_name := resource ->> 'resourceType'; resource_name := resource ->> 'resourceType';
resource_amount := (resource ->> 'resourceAmount')::float8; resource_amount := (resource ->> 'resourceAmount')::integer;
sale_result := null; sale_result := null;
begin begin
@ -103,9 +98,9 @@ begin
where key = resource_name || 'ToMoonbucks'; where key = resource_name || 'ToMoonbucks';
update bank_account update bank_account
set balance = balance + resource_amount / resource_sell_factor set balance = balance + resource_amount * resource_sell_factor
where user_id = p_user_id where user_id = p_user_id
returning resource_amount / resource_sell_factor into return_amount; returning resource_amount * resource_sell_factor into return_amount;
sale_result := 'Success'; sale_result := 'Success';
end if; end if;
@ -223,9 +218,9 @@ declare
new_source_id uuid; new_source_id uuid;
res_id integer; res_id integer;
address varchar; address varchar;
price float8; price integer;
min_res float8; min_res integer;
max_res float8; max_res integer;
begin begin
select value into price from game_constants where key = 'price'; select value into price from game_constants where key = 'price';
@ -267,7 +262,7 @@ returns table (
id uuid, id uuid,
name varchar, name varchar,
wallet varchar, wallet varchar,
balance float8 balance integer
) )
as $$ as $$
begin begin
@ -280,6 +275,7 @@ begin
end; end;
$$ language plpgsql; $$ language plpgsql;
create or replace function purchase_item( create or replace function purchase_item(
p_user_id uuid, p_user_id uuid,
p_store_item_id integer p_store_item_id integer
@ -287,7 +283,7 @@ create or replace function purchase_item(
as $$ as $$
declare declare
new_item_id uuid; new_item_id uuid;
item_price float8; item_price integer;
begin begin
select price into item_price from store_item where store_item.id = p_store_item_id; select price into item_price from store_item where store_item.id = p_store_item_id;
@ -318,7 +314,7 @@ as $$
declare declare
upgrade_event_id uuid; upgrade_event_id uuid;
item_insert_id uuid; item_insert_id uuid;
upgrade_price float8; upgrade_price integer;
begin begin
select upgrade_item.price into upgrade_price select upgrade_item.price into upgrade_price
@ -365,7 +361,7 @@ declare
inv_id uuid; inv_id uuid;
stake_id uuid; stake_id uuid;
stake_created_at timestamp; stake_created_at timestamp;
total_stake_amount float8; total_stake_amount integer;
found_resource_well_id uuid; found_resource_well_id uuid;
begin begin
@ -396,7 +392,7 @@ begin
raise exception 'Unable to find an inventory item that is owned and not actively staked'; raise exception 'Unable to find an inventory item that is owned and not actively staked';
end if; end if;
select claim_amount + coalesce(claim_boost, 0) select claim_amount
into total_stake_amount into total_stake_amount
from inventory_item from inventory_item
join store_item on inventory_item.store_item_id = store_item.id join store_item on inventory_item.store_item_id = store_item.id
@ -429,7 +425,7 @@ returns table (
"wellId" uuid, "wellId" uuid,
"inventoryItemId" uuid, "inventoryItemId" uuid,
"resourceType" varchar, "resourceType" varchar,
"stakeAmount" float8, "stakeAmount" integer,
"stakeTime" timestamp with time zone, "stakeTime" timestamp with time zone,
"durationInMins" integer, "durationInMins" integer,
unclaimed bool unclaimed bool
@ -463,8 +459,8 @@ declare
stake record; stake record;
claim_event_id uuid; claim_event_id uuid;
stake_end_time timestamp; stake_end_time timestamp;
resource_supply float8; resource_supply integer;
final_supply float8; final_supply integer;
begin begin
select select
staking_event.id, staking_event.id,

View File

@ -18,9 +18,9 @@ create table store_item(
id integer primary key, id integer primary key,
name text not null, name text not null,
description text not null, description text not null,
price float8 not null, price integer not null,
image_name text not null, image_name text not null,
claim_amount float8 not null, claim_amount integer not null,
completion_time_in_mins integer not null completion_time_in_mins integer not null
); );
@ -28,7 +28,7 @@ create table upgrade_item(
tier integer not null, tier integer not null,
store_item_id integer not null, store_item_id integer not null,
price text not null, price text not null,
claim_boost float8 not null, claim_boost integer not null,
constraint fk_store_item_upgrade_item foreign key(store_item_id) constraint fk_store_item_upgrade_item foreign key(store_item_id)
references store_item(id) on delete cascade, references store_item(id) on delete cascade,
primary key (store_item_id, tier) primary key (store_item_id, tier)
@ -57,7 +57,7 @@ create table resource_well (
id uuid primary key default gen_random_uuid(), id uuid primary key default gen_random_uuid(),
resource_id integer not null, resource_id integer not null,
source_id uuid not null, source_id uuid not null,
supply float8 not null, supply integer not null,
constraint fk_sid_resource_well foreign key(source_id) constraint fk_sid_resource_well foreign key(source_id)
references staking_source(id) on delete cascade, references staking_source(id) on delete cascade,
constraint fk_rid_resource_well foreign key(resource_id) constraint fk_rid_resource_well foreign key(resource_id)
@ -91,12 +91,12 @@ create table staking_event (
well_id uuid not null, well_id uuid not null,
inventory_item_id uuid not null, inventory_item_id uuid not null,
duration_in_mins integer not null, duration_in_mins integer not null,
stake_amount float8 not null, stake_amount integer not null,
created_at timestamp with time zone default timezone('utc', now()), created_at timestamp with time zone default timezone('utc', now()),
constraint fk_user_staking_event foreign key(user_id) constraint fk_user_staking_event foreign key(user_id)
references users(id) on delete cascade, references users(id) on delete cascade,
constraint fk_wid_staking_event foreign key(well_id) constraint fk_wid_staking_event foreign key(well_id)
references resource_well(id) on delete cascade, references resource_well(id),
constraint fk_iiid_staking_event foreign key(inventory_item_id) constraint fk_iiid_staking_event foreign key(inventory_item_id)
references inventory_item(id) on delete cascade references inventory_item(id) on delete cascade
); );
@ -104,7 +104,7 @@ create table staking_event (
create table claim_event ( create table claim_event (
id uuid primary key default gen_random_uuid(), id uuid primary key default gen_random_uuid(),
staking_event_id uuid not null, staking_event_id uuid not null,
claim_amount float8 not null, claim_amount integer not null,
created_at timestamp with time zone default timezone('utc', now()), created_at timestamp with time zone default timezone('utc', now()),
constraint fk_se_claim_event foreign key(staking_event_id) constraint fk_se_claim_event foreign key(staking_event_id)
references staking_event(id) on delete cascade references staking_event(id) on delete cascade
@ -113,7 +113,7 @@ create table claim_event (
create table bank_account ( create table bank_account (
id uuid primary key default gen_random_uuid(), id uuid primary key default gen_random_uuid(),
user_id uuid not null, user_id uuid not null,
balance float8 not null default 0 check (balance >= 0), balance integer not null default 0 check (balance >= 0),
constraint fk_user_bank_account foreign key(user_id) constraint fk_user_bank_account foreign key(user_id)
references users(id) on delete cascade references users(id) on delete cascade
); );
@ -122,7 +122,7 @@ create table resource_account (
id uuid primary key default gen_random_uuid(), id uuid primary key default gen_random_uuid(),
resource_id integer not null, resource_id integer not null,
user_id uuid not null, user_id uuid not null,
balance float8 not null default 0 check (balance >= 0), balance integer not null default 0 check (balance >= 0),
constraint fk_user_resource_account foreign key(user_id) constraint fk_user_resource_account foreign key(user_id)
references users(id) on delete cascade, references users(id) on delete cascade,
constraint fk_rid_resource_account foreign key(resource_id) constraint fk_rid_resource_account foreign key(resource_id)

View File

@ -1,9 +1,8 @@
"use client"; "use client";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { IResourceAccount, IConversionPair, IGameConfig } from "typings"; import { IResourceAccount, IConversionPair, IGameConfig } from "typings";
import { resourceToFc, resourceToBg } from "../../../utils/helpers"; import { resourceToFc } from "../../../utils/helpers";
import ResourceModal from "./ResourceModal"; import ResourceModal from "./ResourceModal";
import { BiLoaderAlt } from "react-icons/bi";
const ResourceAccount = (props: { const ResourceAccount = (props: {
account: IResourceAccount; account: IResourceAccount;
@ -14,14 +13,13 @@ const ResourceAccount = (props: {
const [conversionPair, setConversionPair] = useState< const [conversionPair, setConversionPair] = useState<
IConversionPair | undefined IConversionPair | undefined
>(undefined); >(undefined);
const [conversionRate, setConversionRate] = useState<number>(0); const [conversionRate, setConversionRate] = useState<number>(0.1);
const [isLoading, setIsLoading] = useState(false);
const handleConversionPair = (amount: number, resourceType: string) => { const handleConversionPair = (amount: number, resourceType: string) => {
const updatedPair = { const updatedPair = {
resourceType: resourceType, resourceType: resourceType,
resourceAmount: amount, resourceAmount: amount,
moneyAmount: amount / conversionRate, moneyAmount: amount * conversionRate,
}; };
setConversionPair(updatedPair); setConversionPair(updatedPair);
}; };
@ -33,22 +31,6 @@ const ResourceAccount = (props: {
return rate; return rate;
}; };
const handleSellResource = async () => {
if (conversionPair) {
setIsLoading(true);
try {
await props.sellResource([conversionPair]);
} catch (error) {
// Show error here
} finally {
setShowModal(false);
}
conversionPair && props.sellResource([conversionPair]);
setIsLoading(false);
setShowModal(false);
}
};
useEffect(() => { useEffect(() => {
const rate = getConversionRate(props.account); const rate = getConversionRate(props.account);
setConversionRate(rate); setConversionRate(rate);
@ -80,12 +62,7 @@ const ResourceAccount = (props: {
/> />
</svg> </svg>
</button> </button>
<div className="relative md:h-auto max-w-md max-h-full overflow-y-auto"> <div className="md:h-auto max-w-md max-h-full overflow-y-auto">
{isLoading && (
<div className="absolute bg-white/75 text-white w-full h-full grid justify-center items-center rounded-xl">
<BiLoaderAlt className="animate-spin w-6 h-6" />
</div>
)}
<ResourceModal <ResourceModal
resourceAccount={props.account} resourceAccount={props.account}
conversionPair={conversionPair} conversionPair={conversionPair}
@ -107,7 +84,7 @@ const ResourceAccount = (props: {
</p> </p>
{conversionPair && ( {conversionPair && (
<button <button
onClick={() => handleSellResource()} onClick={() => props.sellResource([conversionPair])}
className="bg-green-700 py-2 text-slate-900 rounded-lg font-bold w-28 text-center text-white" className="bg-green-700 py-2 text-slate-900 rounded-lg font-bold w-28 text-center text-white"
> >
Sell Now Sell Now
@ -119,14 +96,8 @@ const ResourceAccount = (props: {
</div> </div>
</div> </div>
)} )}
<div className="flex-1 md:mb-0 mb-2 h-auto p-1 rounded-xl"> <div className="flex-1 md:mb-0 mb-2 bg-gradient-to-tr from-purple-600 via-blue-600 to-indigo-700 h-auto p-1 rounded-xl">
<div <div className="relative bg-slate-900 text-white px-8 py-4 rounded-xl">
className={
"bg-gradient-to-br hover:bg-gradient-to-tr " +
resourceToBg(props.account.resourceType) +
" relative text-white px-8 py-4 rounded-xl"
}
>
<span <span
className={resourceToFc(props.account.resourceType) + " font-bold"} className={resourceToFc(props.account.resourceType) + " font-bold"}
> >
@ -139,30 +110,23 @@ const ResourceAccount = (props: {
})}{" "} })}{" "}
kg kg
</h3> </h3>
<div className="absolute right-0 top-0 mt-4 mr-4 hover:cursor-pointer"> <svg
<div xmlns="http://www.w3.org/2000/svg"
className="flex" fill="none"
onClick={() => { viewBox="0 0 24 24"
setShowModal(!showModal); strokeWidth={1.5}
}} stroke="currentColor"
> className="w-6 h-6 absolute right-0 top-0 mt-4 mr-4 hover:cursor-pointer"
<span className="mr-2">Sell</span> onClick={() => {
<svg setShowModal(!showModal);
xmlns="http://www.w3.org/2000/svg" }}
fill="none" >
viewBox="0 0 24 24" <path
strokeWidth={1.5} strokeLinecap="round"
stroke="currentColor" strokeLinejoin="round"
className="w-6 h-6" d="M2.25 18.75a60.07 60.07 0 0115.797 2.101c.727.198 1.453-.342 1.453-1.096V18.75M3.75 4.5v.75A.75.75 0 013 6h-.75m0 0v-.375c0-.621.504-1.125 1.125-1.125H20.25M2.25 6v9m18-10.5v.75c0 .414.336.75.75.75h.75m-1.5-1.5h.375c.621 0 1.125.504 1.125 1.125v9.75c0 .621-.504 1.125-1.125 1.125h-.375m1.5-1.5H21a.75.75 0 00-.75.75v.75m0 0H3.75m0 0h-.375a1.125 1.125 0 01-1.125-1.125V15m1.5 1.5v-.75A.75.75 0 003 15h-.75M15 10.5a3 3 0 11-6 0 3 3 0 016 0zm3 0h.008v.008H18V10.5zm-12 0h.008v.008H6V10.5z"
> />
<path </svg>
strokeLinecap="round"
strokeLinejoin="round"
d="M2.25 18.75a60.07 60.07 0 0115.797 2.101c.727.198 1.453-.342 1.453-1.096V18.75M3.75 4.5v.75A.75.75 0 013 6h-.75m0 0v-.375c0-.621.504-1.125 1.125-1.125H20.25M2.25 6v9m18-10.5v.75c0 .414.336.75.75.75h.75m-1.5-1.5h.375c.621 0 1.125.504 1.125 1.125v9.75c0 .621-.504 1.125-1.125 1.125h-.375m1.5-1.5H21a.75.75 0 00-.75.75v.75m0 0H3.75m0 0h-.375a1.125 1.125 0 01-1.125-1.125V15m1.5 1.5v-.75A.75.75 0 003 15h-.75M15 10.5a3 3 0 11-6 0 3 3 0 016 0zm3 0h.008v.008H18V10.5zm-12 0h.008v.008H6V10.5z"
/>
</svg>
</div>
</div>
</div> </div>
</div> </div>
</> </>

View File

@ -1,6 +1,11 @@
"use client"; "use client";
import React from "react"; import React, { useState, useEffect } from "react";
import { IBankAccount, IConversionPair, IGameConfig } from "typings"; import {
IBankAccount,
IConversionPair,
IGameConfig,
IResourceAccount,
} from "typings";
import Account from "./Account"; import Account from "./Account";
import { BiLoaderAlt } from "react-icons/bi"; import { BiLoaderAlt } from "react-icons/bi";

View File

@ -2,8 +2,6 @@ import React, { useState, useEffect } from "react";
import { IInventoryItem, IStake, IUpgrade } from "typings"; import { IInventoryItem, IStake, IUpgrade } from "typings";
import CardLayout from "../../Layouts/CardLayout"; import CardLayout from "../../Layouts/CardLayout";
import Image from "next/image"; import Image from "next/image";
import { BiLoaderAlt } from "react-icons/bi";
const InventoryItem = (props: { const InventoryItem = (props: {
inventoryItem: IInventoryItem; inventoryItem: IInventoryItem;
stakes: IStake[] | null; stakes: IStake[] | null;
@ -21,75 +19,19 @@ const InventoryItem = (props: {
setCurrentUpgrade(currentUpdade); setCurrentUpgrade(currentUpdade);
}, [props.inventoryItem]); }, [props.inventoryItem]);
const [isLoading, setIsLoading] = useState(false); const isInUse = () => {
if (!props.stakes) return false;
const handleUpgradeInventoryItem = async () => { return props.stakes.some((stake) => {
setIsLoading(true); stake.inventoryItemId === props.inventoryItem.id;
try { });
// Create the source
await props.upgradeInventoryItem(
props.inventoryItem.id,
props.inventoryItem.storeItem.id
);
} catch (error) {
// Handle error if needed (e.g., show a notification or log the error)
} finally {
setIsLoading(false);
}
}; };
const renderUpgradeButton = () => { const getNextUpgradePrice = () => {
// If no staking source
if (!props.stakes) return "No staking source";
// If item is in use
if (
props.stakes.some((stake) => {
stake.inventoryItemId === props.inventoryItem.id;
})
)
return "In use";
// If item is maxxed
if (currentTierIndex === 4) return "Maxed"; if (currentTierIndex === 4) return "Maxed";
return `$${
// Show price props.inventoryItem.storeItem.upgrades[currentTierIndex + 1].price
return ( }`;
<>
{isLoading ? (
<button className="bg-green-600 rounded-full p-1">
<BiLoaderAlt className="animate-spin w-6 h-6" />
</button>
) : (
<button
onClick={() => handleUpgradeInventoryItem()}
className="bg-green-600 rounded-full p-1"
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={2.5}
stroke="currentColor"
className="w-6 h-6"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M12 19.5v-15m0 0l-6.75 6.75M12 4.5l6.75 6.75"
/>
</svg>
</button>
)}
<span className="ml-1">
{`$${
props.inventoryItem.storeItem.upgrades[currentTierIndex + 1].price
}`}
</span>
</>
);
}; };
return ( return (
<CardLayout> <CardLayout>
<div className="md:flex gap-4"> <div className="md:flex gap-4">
@ -100,42 +42,59 @@ const InventoryItem = (props: {
<p className="text-sm mb-3"> <p className="text-sm mb-3">
{props.inventoryItem.storeItem.description} {props.inventoryItem.storeItem.description}
</p> </p>
<div className="grid grid-cols-4 gap-4"> <p className="font-bold mt-4">Base Yield</p>
<div className="col-span-1"> <p>{props.inventoryItem.storeItem.claimAmount}</p>
<p className="font-bold mt-4">Yield</p> <p className="font-bold mb-1 mt-4">Tier</p>
<p> <div className="flex items-center">
{props.inventoryItem.storeItem.claimAmount} (+ {currentUpgrade &&
{currentUpgrade && currentUpgrade.claimBoost}) props.inventoryItem.storeItem.upgrades.map((upgrade, id) => {
</p> if (upgrade.tier <= currentUpgrade.tier) {
</div> return (
<div className="col-span-3"> <span key={id} className="bg-green-600 px-4 py-2"></span>
<p className="font-bold mb-1 mt-4">Tier</p> );
<div className="flex"> } else {
<div className="flex items-center mr-2"> return (
{currentUpgrade && <span key={id} className="bg-slate-200 px-4 py-2"></span>
props.inventoryItem.storeItem.upgrades.map( );
(upgrade, id) => { }
if (upgrade.tier <= currentUpgrade.tier) { })}
return ( <p className="font-bold ml-1">
<span (+{currentUpgrade && currentUpgrade.claimBoost})
key={id} </p>
className="bg-green-600 px-2 py-2 border border-black" </div>
></span> <p className="font-bold mb-1 mt-4">Upgrade</p>
); <div className="flex items-center">
} else { {isInUse() ? (
return ( <button className="bg-slate-400 text-slate-600 px-4 py-2 rounded-lg font-bold text-center cursor-not-allowed">
<span In Use
key={id} </button>
className="bg-slate-200 px-2 py-2 border border-black" ) : (
></span> <button
); onClick={() =>
} props.upgradeInventoryItem(
} props.inventoryItem.id,
)} props.inventoryItem.storeItem.id
</div> )
<div className="flex items-center">{renderUpgradeButton()}</div> }
</div> className="bg-green-600 rounded-full p-1"
</div> >
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={2.5}
stroke="currentColor"
className="w-6 h-6"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M12 19.5v-15m0 0l-6.75 6.75M12 4.5l6.75 6.75"
/>
</svg>
</button>
)}
<span className="ml-1">({getNextUpgradePrice()})</span>
</div> </div>
</div> </div>
<div className="relative object-contain w-48 h-48 mt-4 md:mt-0"> <div className="relative object-contain w-48 h-48 mt-4 md:mt-0">

View File

@ -23,14 +23,14 @@ const NavbarVertical: React.FC<NavbarProps> = ({
return ( return (
<div className="w-full col-span-5 md:col-span-1"> <div className="w-full col-span-5 md:col-span-1">
<nav className="bg-gradient-to-tr from-purple-600 via-blue-600 to-indigo-700 h-auto p-1 rounded-xl"> <nav className="bg-gradient-to-tr from-purple-600 via-blue-600 to-indigo-700 h-auto p-1 rounded-xl">
<ul className="bg-slate-900 rounded-xl p-2"> <ul className="bg-slate-900 p-8 rounded-xl">
{menuItems.map((item) => ( {menuItems.map((item) => (
<li <li
key={item.componentKey} key={item.componentKey}
className={`cursor-pointer mb-1 px-4 ${ className={`text-white cursor-pointer mb-1 ${
item.componentKey === activeItem item.componentKey === activeItem
? "text-white bg-gradient-to-tr from-purple-600 via-blue-600 to-indigo-700 text-3xl font-bold rounded-xl py-2 " ? "text-3xl font-bold"
: "text-white text-2xl" : "text-2xl"
}`} }`}
onClick={() => onMenuItemClick(item.componentKey)} onClick={() => onMenuItemClick(item.componentKey)}
> >

View File

@ -23,10 +23,7 @@ const SelectDropdown: React.FC<ISelectDropdownProps> = (props) => {
onChange={handleChange} onChange={handleChange}
className="text-black flex-1 p-2 border border-gray-300 rounded-lg hover:cursor-pointer" className="text-black flex-1 p-2 border border-gray-300 rounded-lg hover:cursor-pointer"
> >
<option value="" className="font-bold"> <option value="">Select</option>
Select resource to mine
</option>
<hr />
{props.options.map((option, index) => ( {props.options.map((option, index) => (
<option key={index} value={option.value}> <option key={index} value={option.value}>
{option.label} {option.label}

View File

@ -6,7 +6,6 @@ import {
} from "../../../utils/helpers"; } from "../../../utils/helpers";
import SelectDropdown from "../SelectDropdown"; import SelectDropdown from "../SelectDropdown";
import Image from "next/image"; import Image from "next/image";
import { BiLoaderAlt } from "react-icons/bi";
const StakingSource = (props: { const StakingSource = (props: {
stakingSource: IStakingSource; stakingSource: IStakingSource;
@ -26,7 +25,7 @@ const StakingSource = (props: {
null null
); );
const [selectedWellId, setSelectedWellId] = useState<string | null>(null); const [selectedWellId, setSelectedWellId] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(false); const [showInfo, setShowInfo] = useState(false);
// Check if claimable every second // Check if claimable every second
useEffect(() => { useEffect(() => {
@ -55,33 +54,18 @@ const StakingSource = (props: {
}; };
}); });
const handleStartMining = async () => { const handleStartMining = () => {
if (selectedInventoryItemId && selectedWellId && selectedStoreItemId) { if (selectedInventoryItemId && selectedWellId && selectedStoreItemId) {
setIsLoading(true); props.startStake(
selectedInventoryItemId,
try { selectedStoreItemId,
await props.startStake( selectedWellId
selectedInventoryItemId, );
selectedStoreItemId,
selectedWellId
);
} catch (error) {
// Handle error if needed (e.g., show a notification or log the error)
} finally {
setIsLoading(false);
}
} }
}; };
const handleClaim = async (stakingEventId: number) => { const handleClaim = (stakingEventId: number) => {
setIsLoading(true); props.claimStake(stakingEventId);
try {
await props.claimStake(stakingEventId);
} catch (error) {
// Handle error if needed (e.g., show a notification or log the error)
} finally {
setIsLoading(false);
}
}; };
const handleSelectChange = ( const handleSelectChange = (
@ -118,7 +102,7 @@ const StakingSource = (props: {
return ( return (
<button <button
onClick={() => handleClaim(props.stake.id)} onClick={() => handleClaim(props.stake.id)}
className="bg-green-600 text-white px-4 py-2 rounded-lg font-bold text-center" className="bg-slate-100 text-slate-900 px-4 py-2 rounded-lg font-bold text-center"
> >
Claim Claim
</button> </button>
@ -135,112 +119,122 @@ const StakingSource = (props: {
}; };
return ( return (
<div className="flex"> <div className="flex justify-center items-center">
<Image <div className="relative group">
src={"/assets/moons/" + props.stakingSource.image} <Image
alt="Moon" src={"/assets/moons/" + props.stakingSource.image}
width={300} alt="Moon"
height={300} width={props.stakingSource.size}
style={{ objectFit: "contain" }} height={props.stakingSource.size}
className="mr-4" onClick={() => setShowInfo(!showInfo)}
/> className="hover:cursor-pointer"
<div className="max-w-md text-white bg-gradient-to-tr from-purple-600 via-blue-600 to-indigo-700 md:rounded-xl p-1"> />
<div className="relative bg-slate-900 md:rounded-xl h-full"> {showInfo && (
{isLoading && ( <div className="z-10 fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center">
<div className="absolute bg-slate-900 w-full h-full grid justify-center items-center rounded-xl"> <div className="w-full h-full md:h-auto max-w-md max-h-full overflow-y-auto">
<BiLoaderAlt className="animate-spin w-6 h-6" /> <div className="relative h-full md:h-auto text-white bg-gradient-to-tr from-purple-600 via-blue-600 to-indigo-700 h-auto md:rounded-xl h-auto p-1">
</div> <div className="h-full md:h-auto bg-slate-900 md:rounded-xl p-4">
)} <button
<div className="p-4"> onClick={() => {
<h3 className="text-3xl font-bold mb-2"> setShowInfo(!showInfo);
{props.stakingSource.name}{" "} }}
<span> className="text-white absolute -right-0 mr-4"
(
{props.stakingSource.resourceWells.reduce(
(x, y) => x + y.supply,
0
)}
)
</span>
</h3>
<p className="text-sm">{props.stakingSource.description}</p>
</div>
<div className="p-4">
{activeStakes.length > 0 && (
<>
{activeStakes.map((stake, id) => (
<div
key={id}
className="border border-white rounded-xl p-3 mb-2 bg-black/20"
> >
<p className="font-bold text-xl mb-2"> <svg
{props.inventoryItems && xmlns="http://www.w3.org/2000/svg"
getObjectFromArray( fill="none"
props.inventoryItems, viewBox="0 0 24 24"
"id", strokeWidth={2}
stake.inventoryItemId stroke="currentColor"
)?.storeItem.name} className="w-6 h-6"
</p> >
<p> <path
<span className="font-bold">Resource: </span> strokeLinecap="round"
{stake.resourceType} strokeLinejoin="round"
</p> d="M6 18L18 6M6 6l12 12"
<p className="mb-2"> />
<span className="font-bold">Yield: </span> </svg>
{stake.stakeAmount} </button>
</p> <h3 className="text-3xl font-bold mb-2">
<RenderButtonOrCountdown stake={stake} /> {props.stakingSource.name}
</div> </h3>
))} <p className="text-sm">{props.stakingSource.description}</p>
</> <div className="flex-1">
)} <p className="font-bold mt-4 mb-2">Active Drills</p>
{props.inventoryItems && ( {activeStakes &&
<div className=""> activeStakes.map((stake, id) => (
{props.inventoryItems.map( <div
(item, id) => key={id}
!isActive(item) && ( className="border border-white rounded-xl p-3 mb-2 bg-black/20"
<div >
key={id} <p>
className="border border-white rounded-xl p-3 mb-2 bg-black/20" <span className="font-bold">Drill: </span>
> {props.inventoryItems &&
<p className="font-bold text-xl mb-2"> getObjectFromArray(
{item.storeItem.name} props.inventoryItems,
</p> "id",
<div className="flex"> stake.inventoryItemId
<SelectDropdown )?.storeItem.name}
options={props.stakingSource.resourceWells.map( </p>
(well): IOption => ({ <p>
value: well.id, <span className="font-bold">Resource: </span>
label: well.resourceType + ` (${well.supply})`, {stake.resourceType}
}) </p>
)} <p className="mb-2">
onChange={(value) => <span className="font-bold">Yield: </span>
handleSelectChange( {stake.stakeAmount}
value, </p>
item.id, <RenderButtonOrCountdown stake={stake} />
item.storeItem.id
)
}
isActive={selectedInventoryItemId === item.id}
/>
{selectedInventoryItemId === item.id ? (
<button
onClick={handleStartMining}
className="bg-slate-100 text-slate-900 px-4 py-2 rounded-lg font-bold w-28 text-center ml-2"
>
Activate
</button>
) : (
<></>
)}
</div> </div>
</div> ))}
) </div>
)} <p className="font-bold mt-4 mb-2">Inactive Drills</p>
{props.inventoryItems &&
props.inventoryItems.map(
(item, id) =>
!isActive(item) && (
<div
key={id}
className="border border-white rounded-xl p-3 mb-2 bg-black/20"
>
<p className="font-bold">{item.storeItem.name}</p>
<p className="mt-2">Select Resource</p>
<div className="flex">
<SelectDropdown
options={props.stakingSource.resourceWells.map(
(well): IOption => ({
value: well.id,
label: well.resourceType,
})
)}
onChange={(value) =>
handleSelectChange(
value,
item.id,
item.storeItem.id
)
}
isActive={selectedInventoryItemId === item.id}
/>
{selectedInventoryItemId === item.id ? (
<button
onClick={handleStartMining}
className="bg-slate-100 text-slate-900 px-4 py-2 rounded-lg font-bold w-28 text-center ml-2"
>
Activate
</button>
) : (
<></>
)}
</div>
</div>
)
)}
</div>
</div> </div>
)} </div>
</div> </div>
</div> )}
</div> </div>
</div> </div>
); );

View File

@ -1,8 +1,42 @@
"use client"; "use client";
import React, { useState } from "react"; import React from "react";
import { IInventoryItem, IStakingSource } from "typings"; import { IInventoryItem, IStakingSource } from "typings";
import StakingSource from "./StakingSource"; import StakingSource from "./StakingSource";
import { BiLoaderAlt } from "react-icons/bi";
interface RowProps {
stakingSources: IStakingSource[];
inventoryItems: IInventoryItem[] | undefined | null;
claimStake: (stakingEventId: number) => void;
startStake: (
inventoryItemId: string,
storeItemId: number,
wellId: string
) => void;
}
const Row: React.FC<RowProps> = ({
stakingSources,
inventoryItems,
claimStake,
startStake,
}) => {
return (
<div className="md:flex md:justify-between md:items-center w-full mb-8 mt-8">
{stakingSources.map((stakingSource, index) => (
<div key={index} className="w-full">
<StakingSource
stakingSource={stakingSource}
inventoryItems={inventoryItems}
claimStake={claimStake}
startStake={startStake}
/>
</div>
))}
</div>
);
};
const moonSizes = [250, 375, 300, 225, 175, 300, 200, 150, 300, 225];
const StakingSourcesView = (props: { const StakingSourcesView = (props: {
stakingSources: IStakingSource[] | null; stakingSources: IStakingSource[] | null;
@ -15,67 +49,72 @@ const StakingSourcesView = (props: {
) => void; ) => void;
createStakingSource: () => void; createStakingSource: () => void;
}) => { }) => {
const [isLoading, setIsLoading] = useState(false); // Generate rows of stakingSources
const handleRows = () => {
let rows = [];
let currentRow: IStakingSource[] = [];
let rowSize = 3;
if (props.stakingSources) {
props.stakingSources.forEach((stakingSource, index) => {
currentRow.push({ ...stakingSource, size: moonSizes[index] });
const handleCreateStakingSource = async () => { if (currentRow.length === rowSize) {
setIsLoading(true); rows.push(currentRow);
try { currentRow = [];
// Create the source rowSize = rowSize === 3 ? 4 : 3;
await props.createStakingSource(); }
} catch (error) { });
// Handle error if needed (e.g., show a notification or log the error)
} finally { if (currentRow.length) {
setIsLoading(false); rows.push(currentRow);
}
return rows;
} else {
return [];
} }
}; };
const rows = handleRows();
return ( return (
<div className="bg-gradient-to-tr from-purple-600 via-blue-600 to-indigo-700 h-auto p-1 rounded-xl h-auto p-1 rounded-xl col-span-5"> <div className="bg-gradient-to-tr from-purple-600 via-blue-600 to-indigo-700 h-auto p-1 rounded-xl h-auto p-1 rounded-xl col-span-5">
<div className="bg-slate-900 p-8 rounded-xl h-full"> <div className="bg-slate-900 p-8 rounded-xl h-full">
<div className="flex items-center mb-4"> <div className="flex items-center mb-4">
<h2 className="text-3xl text-white font-bold">Your Moons</h2> <h2 className="text-3xl text-white font-bold">Your Moons</h2>
{isLoading ? ( <button
<button className="bg-green-600 text-white rounded-full ml-2 p-1 inline"> className="bg-green-600 rounded-full ml-2 p-1 inline"
<BiLoaderAlt className="animate-spin w-6 h-6" /> onClick={() => props.createStakingSource()}
</button> >
) : ( <svg
<button xmlns="http://www.w3.org/2000/svg"
className="bg-green-600 text-white rounded-full ml-2 p-1 inline" fill="none"
onClick={() => handleCreateStakingSource()} viewBox="0 0 24 24"
strokeWidth={2}
stroke="currentColor"
className="w-6 h-6"
> >
<svg <path
xmlns="http://www.w3.org/2000/svg" strokeLinecap="round"
fill="none" strokeLinejoin="round"
viewBox="0 0 24 24" d="M12 4.5v15m7.5-7.5h-15"
strokeWidth={2} />
stroke="currentColor" </svg>
className="w-6 h-6" </button>
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M12 4.5v15m7.5-7.5h-15"
/>
</svg>
</button>
)}
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
{props.stakingSources ? (
props.stakingSources.map((stakingSource, index) => (
<div key={index} className="mb-4">
<StakingSource
stakingSource={stakingSource}
inventoryItems={props.inventoryItems}
claimStake={props.claimStake}
startStake={props.startStake}
/>
</div>
))
) : (
<p className="text-white text-2xl">You don't have any moons</p>
)}
</div> </div>
{rows.length > 0 ? (
rows.map((stakingSources, index) => (
<Row
key={index}
stakingSources={stakingSources}
inventoryItems={props.inventoryItems}
claimStake={props.claimStake}
startStake={props.startStake}
/>
))
) : (
<p className="text-white text-2xl">You don't have any moons</p>
)}
</div> </div>
</div> </div>
); );

View File

@ -0,0 +1,58 @@
"use client";
import React from "react";
import { IInventoryItem, IStakingSource } from "typings";
import StakingSource from "./StakingSource";
const StakingSourcesView = (props: {
stakingSources: IStakingSource[] | null;
inventoryItems: IInventoryItem[] | null | undefined;
claimStake: (stakingEventId: number) => void;
startStake: (
inventoryItemId: number,
storeItemId: string,
wellId: number
) => void;
createStakingSource: () => void;
}) => {
return (
<div className="bg-slate-800 p-4 rounded-lg text-white col-span-5">
<div className="flex items-center mb-4">
<h2 className="text-3xl font-bold">Your Moons</h2>
<button
className="bg-green-600 rounded-full ml-2 p-1 inline"
onClick={() => props.createStakingSource()}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={2}
stroke="currentColor"
className="w-6 h-6"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M12 4.5v15m7.5-7.5h-15"
/>
</svg>
</button>
</div>
<div className="grid grid-cols-3 gap-4">
{props.stakingSources &&
props.stakingSources.length > 0 &&
props.stakingSources.map((stakingSource, id) => (
<StakingSource
key={id}
stakingSource={stakingSource}
inventoryItems={props.inventoryItems}
claimStake={props.claimStake}
startStake={props.startStake}
/>
))}
</div>
</div>
);
};
export default StakingSourcesView;

View File

@ -1,28 +1,13 @@
"use client"; "use client";
import React, { useState } from "react"; import React from "react";
import { IStoreItem } from "typings"; import { IStoreItem } from "typings";
import CardLayout from "../../Layouts/CardLayout"; import CardLayout from "../../Layouts/CardLayout";
import Image from "next/image"; import Image from "next/image";
import { BiLoaderAlt } from "react-icons/bi";
const StoreItem = (props: { const StoreItem = (props: {
storeItem: IStoreItem; storeItem: IStoreItem;
buyStoreItem: (storeItemId: number) => void; buyStoreItem: (storeItemId: number) => void;
}) => { }) => {
const [isLoading, setIsLoading] = useState(false);
const handleBuyStoreItem = async () => {
setIsLoading(true);
try {
// Create the source
await props.buyStoreItem(props.storeItem.id);
} catch (error) {
// Handle error if needed (e.g., show a notification or log the error)
} finally {
setIsLoading(false);
}
};
return ( return (
<CardLayout> <CardLayout>
<div className="md:flex"> <div className="md:flex">
@ -30,18 +15,26 @@ const StoreItem = (props: {
<h3 className="text-2xl font-bold mb-2">{props.storeItem.name}</h3> <h3 className="text-2xl font-bold mb-2">{props.storeItem.name}</h3>
<p className="text-sm mb-3">{props.storeItem.description}</p> <p className="text-sm mb-3">{props.storeItem.description}</p>
<div className="py-4 my-2"> <div className="py-4 my-2">
{isLoading ? ( <button
<button className="bg-green-600 text-white px-4 py-2 rounded-lg font-bold w-auto text-center"> onClick={() => props.buyStoreItem(props.storeItem.id)}
<BiLoaderAlt className="animate-spin w-6 h-6" /> className="bg-slate-100 text-slate-900 px-4 py-2 rounded-lg font-bold w-auto text-center"
</button> >
) : ( Buy | ${props.storeItem.price}
<button </button>
onClick={() => handleBuyStoreItem()} </div>
className="bg-green-600 text-white px-4 py-2 rounded-lg font-bold w-auto text-center" <div className="flex">
> <div className="mr-3">
Buy | ${props.storeItem.price} <p className="font-bold">Base Yield</p>
</button> <p className="">{props.storeItem.claimAmount}</p>
)} </div>
<div className="">
<p className="font-bold">Upgrades</p>
{props.storeItem.upgrades.map((upgrade, id) => (
<span key={id} className="mr-2">
{upgrade.claimBoost}
</span>
))}
</div>
</div> </div>
</div> </div>
<div className="relative object-contain w-48 h-48"> <div className="relative object-contain w-48 h-48">
@ -53,42 +46,6 @@ const StoreItem = (props: {
/> />
</div> </div>
</div> </div>
<div className="flex flex-grow mt-4">
<div className="mr-3 flex-grow">
<p className="font-bold">Yield</p>
<p className="">{props.storeItem.claimAmount}</p>
</div>
<div className="mr-3 flex-grow">
<p className="font-bold">Completion Time</p>
<p className="">{props.storeItem.completionTimeInMins} min</p>
</div>
<div className="mr-3 flex-grow">
<p className="font-bold">Yield / Hr.</p>
<p className="">
{(
(props.storeItem.claimAmount /
props.storeItem.completionTimeInMins) *
60
).toFixed(0)}
</p>
</div>
</div>
<p className="font-bold mt-4 ">Tiers</p>
<div className="mt-4 flex f-full">
{props.storeItem.upgrades.map((upgrade, id) => (
<table key={id} className="table-auto flex-grow">
<tr className="border">
<td colSpan={2} className="p-2 text-center">
{upgrade.tier}
</td>
</tr>
<tr>
<td className="p-2 border">${upgrade.price}</td>
<td className="p-2 border">+{upgrade.claimBoost}</td>
</tr>
</table>
))}
</div>
</CardLayout> </CardLayout>
); );
}; };

View File

@ -32,7 +32,7 @@ export default function Home() {
const [notificationTime, setNotificationTime] = useState(30); const [notificationTime, setNotificationTime] = useState(30);
const [storeItems, setStoreItems] = useState<IStoreItem[] | null>(null); const [storeItems, setStoreItems] = useState<IStoreItem[] | null>(null);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [activeComponent, setActiveComponent] = useState("stakingsourcesview"); const [activeComponent, setActiveComponent] = useState("inventoryitemview");
const [gameConfig, setGameConfig] = useState<IGameConfig>(); const [gameConfig, setGameConfig] = useState<IGameConfig>();
const isOwned = (storeItemId: number) => { const isOwned = (storeItemId: number) => {
@ -204,13 +204,7 @@ export default function Home() {
if (response.status == 200) { if (response.status == 200) {
const data = await response.json(); const data = await response.json();
setNotification({ setNotification({
message: `You've received ${data[0].returnAmount.toLocaleString( message: `You've received ${data[0].returnAmount} $MoonBucks`,
"en-US",
{
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}
)} $MoonBucks`,
type: "Success", type: "Success",
}); });
fetchBankAccount(); fetchBankAccount();
@ -338,11 +332,7 @@ export default function Home() {
return ( return (
<> <>
<Navbar setUserId={setUserId} /> <Navbar setUserId={setUserId} />
<div className="grid justify-center items-center h-96"> <p>Please login</p>
<h1 className="text-3xl text-white">
Connect Your Wallet to Continue
</h1>
</div>
</> </>
); );

View File

@ -10,11 +10,9 @@ export default function Utils() {
const [userId, setUserId] = useState<string>(""); const [userId, setUserId] = useState<string>("");
const clearUserData = async () => { const clearUserData = async () => {
console.log("Clearing data.."); await fetch(`/api/user/${userId}/clear-data`, {
const response = await fetch(`/api/user/${userId}/clear-data`, {
method: "POST", method: "POST",
}); });
console.log(response);
}; };
const loadConfig = async () => { const loadConfig = async () => {

View File

@ -18,6 +18,6 @@ export default async function handler(
} }
} }
} catch (error) { } catch (error) {
res.status(500).json(error.message); res.status(500).json(error);
} }
} }

View File

@ -2,7 +2,6 @@
Content-Type: application/json Content-Type: application/json
# #
:user_id = 595ab570-fa74-4c6c-980e-4c80d1064dd1 :user_id = 595ab570-fa74-4c6c-980e-4c80d1064dd1
:host = https://moonminers.app/api
# Seed the SQLite database (deprecated) # Seed the SQLite database (deprecated)
GET http://localhost:3000/api/seed GET http://localhost:3000/api/seed
@ -12,12 +11,8 @@ GET http://localhost:3000/api/seed
POST http://localhost:3000/api/import-config POST http://localhost:3000/api/import-config
:headers :headers
# Get leaderboards
GET :host/leaderboards
:headers
# Get Inventory Items # Get Inventory Items
POST :host/user/login POST http://localhost:3000/api/user/login
:headers :headers
{ "wallet" : "Wallet12345678" } { "wallet" : "Wallet12345678" }
@ -45,6 +40,10 @@ PUT http://localhost:3000/api/user/:user_id/account
POST http://localhost:3000/api/user/:user_id/clear-data POST http://localhost:3000/api/user/:user_id/clear-data
:headers :headers
# Get leaderboards
GET http://localhost:3000/api/leaderboards
:headers
# Get game config # Get game config
GET http://localhost:3000/api/get-game-config GET http://localhost:3000/api/get-game-config
:headers :headers
@ -78,8 +77,8 @@ GET http://localhost:3000/api/user/:user_id/stakes/
# Start a stake # Start a stake
POST http://localhost:3000/api/user/:user_id/stakes/start POST http://localhost:3000/api/user/:user_id/stakes/start
:headers :headers
{ "inventoryItemId": "1db181dd-320f-4525-a00d-933e5d72d690" { "inventoryItemId": "2bd5f846-ce1d-45be-998e-26d2e97b7f68"
, "wellId": "0a7fd54d-02cf-4c08-b106-6f812ad4b1ca" } , "wellId": "588ad177-83ca-4a5f-9672-bf97783ea42f" }
# Claim a stake # Claim a stake
POST http://localhost:3000/api/user/:user_id/stakes/claim POST http://localhost:3000/api/user/:user_id/stakes/claim

1
typings.d.ts vendored
View File

@ -39,6 +39,7 @@ export interface IStakingSource {
resourceChance: number | null resourceChance: number | null
resourceMinStartAmount: number | null resourceMinStartAmount: number | null
resourceMaxStartAmount: number | null resourceMaxStartAmount: number | null
size: number | undefined
} }
export interface IInventoryItem { export interface IInventoryItem {