From f01a6b7a18504793954603e437a8c2b91c02a565 Mon Sep 17 00:00:00 2001 From: notV4l Date: Mon, 25 Mar 2024 23:43:13 +0100 Subject: [PATCH] simple admin ui --- scripts/default_auth.sh | 5 +- src/config/ryo.cairo | 8 +- src/config/ryo_address.cairo | 43 +++ src/lib.cairo | 1 + src/models/leaderboard.cairo | 2 +- src/systems/game.cairo | 47 ++- src/systems/leaderboard.cairo | 34 +- src/systems/ryo.cairo | 72 +++- web/manifest.json | 333 ++++++++++++++++-- web/package.json | 1 + web/pnpm-lock.yaml | 79 +++++ web/src/components/layout/Header.tsx | 2 +- web/src/components/pages/admin/DrugTable.tsx | 13 + .../pages/admin/GameLayoutTable.tsx | 29 ++ .../pages/admin/HustlerItemBaseTable.tsx | 13 + .../pages/admin/HustlerItemTiersTable.tsx | 13 + .../pages/admin/PlayerLayoutTable.tsx | 30 ++ web/src/components/pages/admin/tables.ts | 9 + web/src/components/pages/home/ClaimModal.tsx | 18 +- web/src/components/wallet/Connect.tsx | 2 +- web/src/components/wallet/PaperFaucet.tsx | 2 +- web/src/dojo/hooks/useHallOfFame.tsx | 18 +- web/src/dojo/hooks/useSystems.ts | 106 +++++- web/src/dojo/setup/config.ts | 2 +- web/src/dojo/stores/config.tsx | 14 +- web/src/generated/graphql.ts | 134 +++++-- web/src/generated/introspection.ts | 1 + web/src/graphql/config.graphql | 15 +- web/src/pages/admin.tsx | 262 ++++++++++++++ web/src/pages/create/new.tsx | 2 +- web/src/theme/global.tsx | 25 ++ 31 files changed, 1206 insertions(+), 129 deletions(-) create mode 100644 src/config/ryo_address.cairo create mode 100644 web/src/components/pages/admin/DrugTable.tsx create mode 100644 web/src/components/pages/admin/GameLayoutTable.tsx create mode 100644 web/src/components/pages/admin/HustlerItemBaseTable.tsx create mode 100644 web/src/components/pages/admin/HustlerItemTiersTable.tsx create mode 100644 web/src/components/pages/admin/PlayerLayoutTable.tsx create mode 100644 web/src/components/pages/admin/tables.ts create mode 100644 web/src/pages/admin.tsx diff --git a/scripts/default_auth.sh b/scripts/default_auth.sh index ef0a1278a..a386b1bc1 100755 --- a/scripts/default_auth.sh +++ b/scripts/default_auth.sh @@ -18,6 +18,8 @@ export GAME_ADDRESS=$(cat ./target/dev/manifest.json | jq -r '.contracts[] | sel export PAPER_MOCK_ADDRESS=$(cat ./target/dev/manifest.json | jq -r '.contracts[] | select(.name == "rollyourown::_mocks::paper_mock::paper_mock" ).address') +export TREASURY_ADDRESS=$PAPER_MOCK_ADDRESS; + echo "---------------------------------------------------------------------------" echo profile : $PROFILE echo "---------------------------------------------------------------------------" @@ -32,6 +34,7 @@ echo "-------------------------------------------------------------------------- # enable system -> models authorizations sozo -P $PROFILE auth grant --world $WORLD_ADDRESS --wait writer\ RyoConfig,$RYO_ADDRESS \ + RyoAddress,$RYO_ADDRESS \ Leaderboard,$RYO_ADDRESS \ GameConfig,$CONFIG_ADDRESS \ DrugConfig,$CONFIG_ADDRESS \ @@ -55,7 +58,7 @@ sozo -P $PROFILE auth grant --world $WORLD_ADDRESS --wait writer\ echo "Default authorizations have been successfully set." echo "Initializing..." -sozo -P $PROFILE execute --world $WORLD_ADDRESS $RYO_ADDRESS initialize --calldata $PAPER_MOCK_ADDRESS --wait > /dev/null +sozo -P $PROFILE execute --world $WORLD_ADDRESS $RYO_ADDRESS initialize --calldata $PAPER_MOCK_ADDRESS,$TREASURY_ADDRESS --wait > /dev/null echo "Initialized RYO!" sleep $TX_SLEEP diff --git a/src/config/ryo.cairo b/src/config/ryo.cairo index 0d03da1de..d6705994a 100644 --- a/src/config/ryo.cairo +++ b/src/config/ryo.cairo @@ -9,12 +9,13 @@ struct RyoConfig { key: u8, initialized: bool, paused: bool, + // leaderboard_version: u16, leaderboard_duration: u32, - paper_address: ContractAddress, + // paper_fee: u16, -// treasury_address: ContractAddress, -// treasury_pct: u8, + treasury_fee_pct: u8, + treasury_balance: u32, } #[derive(Copy, Drop)] @@ -35,4 +36,5 @@ impl RyoConfigImpl of RyoConfigManagerTrait { fn set(self: RyoConfigManager, ryo_config: RyoConfig) { set!(self.world, (ryo_config)); } + } diff --git a/src/config/ryo_address.cairo b/src/config/ryo_address.cairo new file mode 100644 index 000000000..afc366e8a --- /dev/null +++ b/src/config/ryo_address.cairo @@ -0,0 +1,43 @@ +use starknet::ContractAddress; +use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait}; + +const RYO_ADDRESS_CONFIG_KEY: u8 = 0; + +#[derive(Model, Copy, Drop, Serde)] +struct RyoAddress { + #[key] + key: u8, + paper: ContractAddress, + treasury: ContractAddress, +} + +#[derive(Copy, Drop)] +struct RyoAddressManager { + world: IWorldDispatcher +} + +#[generate_trait] +impl RyoAddressImpl of RyoAddressManagerTrait { + fn new(world: IWorldDispatcher) -> RyoAddressManager { + RyoAddressManager { world } + } + + fn get(self: RyoAddressManager) -> RyoAddress { + get!(self.world, (RYO_ADDRESS_CONFIG_KEY), RyoAddress) + } + + fn set(self: RyoAddressManager, ryo_address: RyoAddress) { + set!(self.world, (ryo_address)); + } + + // getters + + fn paper(self: RyoAddressManager) -> ContractAddress { + self.get().paper + } + + fn treasury(self: RyoAddressManager) -> ContractAddress { + self.get().treasury + } + +} diff --git a/src/lib.cairo b/src/lib.cairo index 09be4379b..cbdd0a8fb 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -5,6 +5,7 @@ mod config { mod game; mod hustlers; mod ryo; + mod ryo_address; } mod models { diff --git a/src/models/leaderboard.cairo b/src/models/leaderboard.cairo index 125271b03..a9ebbfa49 100644 --- a/src/models/leaderboard.cairo +++ b/src/models/leaderboard.cairo @@ -11,6 +11,6 @@ struct Leaderboard { // next_version_timestamp: u64, // - paper_balance: u256, // TODO: use u32 ? + paper_balance: u32, claimed: bool, } diff --git a/src/systems/game.cairo b/src/systems/game.cairo index 7048ad160..12de9e302 100644 --- a/src/systems/game.cairo +++ b/src/systems/game.cairo @@ -25,6 +25,7 @@ trait IGameActions { fn travel(self: @T, game_id: u32, next_location: Locations, actions: Span); fn decide(self: @T, game_id: u32, action: EncounterActions); fn claim(self: @T, season: u16); + fn claim_treasury(self: @T); } @@ -35,7 +36,8 @@ mod game { use rollyourown::{ config::{ drugs::{Drugs}, locations::{Locations}, game::{GameConfig, GameConfigImpl}, - ryo::{RyoConfig, RyoConfigManager, RyoConfigManagerTrait} + ryo::{RyoConfig, RyoConfigManager, RyoConfigManagerTrait}, + ryo_address::{RyoAddress, RyoAddressManager, RyoAddressManagerTrait}, }, models::{ game_store_packed::GameStorePacked, game::{Game, GameImpl}, leaderboard::{Leaderboard} @@ -50,6 +52,7 @@ mod game { }, utils::{random::{Random, RandomImpl}, bytes16::{Bytes16, Bytes16Impl, Bytes16Trait}}, interfaces::paper::{IPaperDispatcher, IPaperDispatcherTrait}, + constants::{ETHER}, }; @@ -332,24 +335,56 @@ mod game { leaderboard.claimed = true; set!(world, (leaderboard)); - // retrieve paper address from ryo_config - let ryo_config_manager = RyoConfigManagerTrait::new(world); - let ryo_config = ryo_config_manager.get(); + // retrieve paper address + let paper_address = RyoAddressManagerTrait::new(world).paper(); + let paper_jackpot_eth: u256 = leaderboard.paper_balance.into() * ETHER; // transfer reward - IPaperDispatcher { contract_address: ryo_config.paper_address } - .transfer(get_caller_address(), leaderboard.paper_balance); + IPaperDispatcher { contract_address: paper_address } + .transfer(get_caller_address(), paper_jackpot_eth); + } + + fn claim_treasury(self: @ContractState) { + // check if owner ??? TODO: check if ok + // self.assert_caller_is_owner(); + + let ryo_config_manager = RyoConfigManagerTrait::new(self.world()); + let mut ryo_config = ryo_config_manager.get(); + + assert(ryo_config.treasury_balance > 0, 'nothin to claim'); + + // calc claimable amount + let claimable_eth: u256 = ryo_config.treasury_balance.into() * ETHER; + + // reset treasury_balance + ryo_config.treasury_balance = 0; + ryo_config_manager.set(ryo_config); + + let ryo_addresses_manager = RyoAddressManagerTrait::new(self.world()); + + // transfer claimable_eth to treasury + IPaperDispatcher { contract_address: ryo_addresses_manager.paper() } + .transfer(ryo_addresses_manager.treasury(), claimable_eth); } } #[generate_trait] impl InternalImpl of InternalTrait { + #[inline(always)] fn assert_not_paused(self: @ContractState) { let ryo_config_manager = RyoConfigManagerTrait::new(self.world()); let ryo_config = ryo_config_manager.get(); assert(!ryo_config.paused, 'game is paused'); } + #[inline(always)] + fn assert_caller_is_owner(self: @ContractState) { + assert( + self.world().is_owner(get_caller_address(), get_contract_address().into()), + 'not owner' + ); + } + fn execute_actions( self: @ContractState, ref game_store: GameStore, ref actions: Span ) { diff --git a/src/systems/leaderboard.cairo b/src/systems/leaderboard.cairo index 6b4114d38..14ec7286a 100644 --- a/src/systems/leaderboard.cairo +++ b/src/systems/leaderboard.cairo @@ -2,9 +2,13 @@ use starknet::{get_caller_address, get_contract_address}; use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait}; use rollyourown::{ - config::{ryo::{RyoConfig, RyoConfigManager, RyoConfigManagerTrait}}, + config::{ + ryo::{RyoConfig, RyoConfigManager, RyoConfigManagerTrait}, + ryo_address::{RyoAddress, RyoAddressManager, RyoAddressManagerTrait} + }, models::{leaderboard::{Leaderboard}}, packing::game_store::{GameStore}, - interfaces::paper::{IPaperDispatcher, IPaperDispatcherTrait}, constants::{ETHER} + interfaces::paper::{IPaperDispatcher, IPaperDispatcherTrait}, constants::{ETHER}, + utils::math::{MathImpl, MathTrait} }; @@ -79,16 +83,28 @@ impl LeaderboardManagerImpl of LeaderboardManagerTrait { // get current leaderboard infos let mut leaderboard = get!(self.world, (ryo_config.leaderboard_version), Leaderboard); - // calc paper_fee - let paper_fee: u256 = ryo_config.paper_fee.into() * ETHER; + // get paper_fee + let paper_fee : u32 = ryo_config.paper_fee.into(); + let paper_fee_eth: u256 = paper_fee.into() * ETHER; - // add paper_fee to current_leaderboard & save - leaderboard.paper_balance += paper_fee; + // calc treasury share + let treasury_share = paper_fee.pct(ryo_config.treasury_fee_pct.into()); + let jackpot_share = paper_fee - treasury_share; + + // add jackpot_share to current_leaderboard & save + leaderboard.paper_balance += jackpot_share; set!(self.world, (leaderboard)); - // transfer paper_fee from user to game ( user approved game contract to spend paper before) - IPaperDispatcher { contract_address: ryo_config.paper_address } - .transfer_from(get_caller_address(), get_contract_address(), paper_fee); + // add treasury_share to treasury_balance & save + ryo_config.treasury_balance += treasury_share; + ryo_config_manager.set(ryo_config); + + // retrieve paper_address + let paper_address = RyoAddressManagerTrait::new(self.world).paper(); + + // transfer paper_fee_ether from user to game ( user approved game contract to spend paper before) + IPaperDispatcher { contract_address: paper_address } + .transfer_from(get_caller_address(), get_contract_address(), paper_fee_eth); ryo_config.leaderboard_version } diff --git a/src/systems/ryo.cairo b/src/systems/ryo.cairo index 7972e2b43..d06b13594 100644 --- a/src/systems/ryo.cairo +++ b/src/systems/ryo.cairo @@ -1,20 +1,23 @@ use starknet::ContractAddress; use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait}; - #[starknet::interface] -trait IRyo { +trait IRyo { // - fn initialize(self: @TContractState, paper_address: ContractAddress); - fn set_paused(self: @TContractState, paused: bool); - fn set_paper_fee(self: @TContractState, fee: u16); - fn set_leaderboard_duration(self: @TContractState, duration_sec: u32); + fn initialize(self: @T, paper_address: ContractAddress, treasury_address: ContractAddress); + fn set_paused(self: @T, paused: bool); + fn set_paper_fee(self: @T, fee: u16); + fn set_treasury_fee_pct(self: @T, fee_pct: u8); + + fn set_leaderboard_duration(self: @T, duration_sec: u32); // - fn paused(self: @TContractState) -> bool; - fn paper(self: @TContractState) -> ContractAddress; - fn paper_fee(self: @TContractState) -> u16; - fn leaderboard_duration(self: @TContractState) -> u32; + fn paper(self: @T) -> ContractAddress; + fn treasury(self: @T) -> ContractAddress; + + fn paused(self: @T) -> bool; + fn paper_fee(self: @T) -> u16; + fn leaderboard_duration(self: @T) -> u32; } #[dojo::contract] @@ -26,7 +29,10 @@ mod ryo { use starknet::info::get_tx_info; use rollyourown::{ - config::{ryo::{RyoConfig, RyoConfigManager, RyoConfigManagerTrait}}, + config::{ + ryo::{RyoConfig, RyoConfigManager, RyoConfigManagerTrait}, + ryo_address::{RyoAddress, RyoAddressManager, RyoAddressManagerTrait}, + }, models::{leaderboard::Leaderboard,}, utils::random::{RandomImpl}, systems::leaderboard::{LeaderboardManager, LeaderboardManagerTrait}, }; @@ -38,7 +44,7 @@ mod ryo { #[abi(embed_v0)] impl RyoExternalImpl of super::IRyo { - fn initialize(self: @ContractState, paper_address: ContractAddress) { + fn initialize(self: @ContractState, paper_address: ContractAddress, treasury_address: ContractAddress) { self.assert_caller_is_owner(); let ryo_config_manager = RyoConfigManagerTrait::new(self.world()); @@ -52,12 +58,25 @@ mod ryo { // RyoConfig ryo_config.initialized = true; ryo_config.paused = false; + ryo_config.leaderboard_version = 1; ryo_config.leaderboard_duration = FEW_MIN; // ONE_WEEK - - ryo_config.paper_address = paper_address; + ryo_config.paper_fee = 1_000; // in ether + ryo_config.treasury_fee_pct = 10; + + // save ryo_config_manager.set(ryo_config); + + // RyoAddresses + let ryo_addresses_manager = RyoAddressManagerTrait::new(self.world()); + let mut ryo_addresses = ryo_addresses_manager.get(); + + ryo_addresses.paper = paper_address; + ryo_addresses.treasury = treasury_address; + + // save + ryo_addresses_manager.set(ryo_addresses); // Leaderboard let leaderboard_manager = LeaderboardManagerTrait::new(self.world()); @@ -88,6 +107,17 @@ mod ryo { ryo_config_manager.set(ryo_config); } + + fn set_treasury_fee_pct(self: @ContractState, fee_pct: u8) { + self.assert_caller_is_owner(); + + let ryo_config_manager = RyoConfigManagerTrait::new(self.world()); + let mut ryo_config = ryo_config_manager.get(); + + ryo_config.treasury_fee_pct = fee_pct; + ryo_config_manager.set(ryo_config); + } + fn set_leaderboard_duration(self: @ContractState, duration_sec: u32) { self.assert_caller_is_owner(); @@ -103,14 +133,18 @@ mod ryo { // getters // - fn paused(self: @ContractState) -> bool { - let ryo_config = RyoConfigManagerTrait::new(self.world()).get(); - ryo_config.paused + fn paper(self: @ContractState) -> ContractAddress { + RyoAddressManagerTrait::new(self.world()).paper() } - fn paper(self: @ContractState) -> ContractAddress { + fn treasury(self: @ContractState) -> ContractAddress { + RyoAddressManagerTrait::new(self.world()).treasury() + } + + + fn paused(self: @ContractState) -> bool { let ryo_config = RyoConfigManagerTrait::new(self.world()).get(); - ryo_config.paper_address + ryo_config.paused } fn paper_fee(self: @ContractState) -> u16 { diff --git a/web/manifest.json b/web/manifest.json index ea13ef90b..b2ba8d8aa 100644 --- a/web/manifest.json +++ b/web/manifest.json @@ -1551,7 +1551,7 @@ { "name": "rollyourown::config::config::config", "address": "0x73e5a7b0f2ff860ae67f9edc7709d3dbbee30f84afcccf1f8e3963e128e1bc5", - "class_hash": "0x4cb81dfced9540cd5b38d7691ea5a9930d04ffcef72f6cd027318129aba94d5", + "class_hash": "0x4e032de3da7ec321e6d1e584d8fe6a8622b0d16922942491b9eda5c5c5a2b29", "abi": [ { "type": "impl", @@ -1871,7 +1871,7 @@ { "name": "rollyourown::systems::devtools::devtools", "address": "0x443bf5fc4480469fa6f52082310e91e90cb86249c1731bb2b4057d77bfcea2", - "class_hash": "0x2de11202d682cfbdb558e40b4d2764fd7aa4a58336c025fc529244647328417", + "class_hash": "0x78077bee04ade4666311e407b37676df6bdcef7e125088c99db93048e272277", "abi": [ { "type": "impl", @@ -2024,7 +2024,7 @@ { "name": "rollyourown::systems::game::game", "address": "0x52d79bdb709ee84fbf0199f427d058977a04c5403f149796ad9d76927a162ec", - "class_hash": "0x35c6f8f8f1bed3c18e260b16a30e6fba9032026a0e2be952c250494f4d0c671", + "class_hash": "0x1561122e4b6050d6732ab4fb0def3d66c5c74f13d7b03df8fdfee562d4c425e", "abi": [ { "type": "impl", @@ -2364,6 +2364,13 @@ ], "outputs": [], "state_mutability": "view" + }, + { + "type": "function", + "name": "claim_treasury", + "inputs": [], + "outputs": [], + "state_mutability": "view" } ] }, @@ -2871,7 +2878,7 @@ { "name": "rollyourown::systems::ryo::ryo", "address": "0x48784839f4ded35f9324f6c49913f4b39a85520eeb8d68279586bb5b90603bf", - "class_hash": "0x47aa88701bc8b87f5b956f6864546688cf21e93455f414a934b3865470084f1", + "class_hash": "0x44baa1c5b1d38d988c4aa8ec75a516f4c75cfc885fc1bac841a76b376b0c6d3", "abi": [ { "type": "impl", @@ -2957,6 +2964,10 @@ { "name": "paper_address", "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "treasury_address", + "type": "core::starknet::contract_address::ContractAddress" } ], "outputs": [], @@ -2986,6 +2997,18 @@ "outputs": [], "state_mutability": "view" }, + { + "type": "function", + "name": "set_treasury_fee_pct", + "inputs": [ + { + "name": "fee_pct", + "type": "core::integer::u8" + } + ], + "outputs": [], + "state_mutability": "view" + }, { "type": "function", "name": "set_leaderboard_duration", @@ -3000,18 +3023,18 @@ }, { "type": "function", - "name": "paused", + "name": "paper", "inputs": [], "outputs": [ { - "type": "core::bool" + "type": "core::starknet::contract_address::ContractAddress" } ], "state_mutability": "view" }, { "type": "function", - "name": "paper", + "name": "treasury", "inputs": [], "outputs": [ { @@ -3020,6 +3043,17 @@ ], "state_mutability": "view" }, + { + "type": "function", + "name": "paused", + "inputs": [], + "outputs": [ + { + "type": "core::bool" + } + ], + "state_mutability": "view" + }, { "type": "function", "name": "paper_fee", @@ -4555,17 +4589,22 @@ "key": false }, { - "name": "paper_address", - "type": "ContractAddress", + "name": "paper_fee", + "type": "u16", "key": false }, { - "name": "paper_fee", - "type": "u16", + "name": "treasury_fee_pct", + "type": "u8", + "key": false + }, + { + "name": "treasury_balance", + "type": "u32", "key": false } ], - "class_hash": "0x49db81215eddd36d4d9285ab5a60aa2ef46741403cd0711c2e7b8f4b6c49349", + "class_hash": "0x45885517184c16daf73bbbd8bbd03b7de1ec183c280182bdab5a20fa436cd31", "abi": [ { "type": "impl", @@ -4778,13 +4817,17 @@ "name": "leaderboard_duration", "type": "core::integer::u32" }, - { - "name": "paper_address", - "type": "core::starknet::contract_address::ContractAddress" - }, { "name": "paper_fee", "type": "core::integer::u16" + }, + { + "name": "treasury_fee_pct", + "type": "core::integer::u8" + }, + { + "name": "treasury_balance", + "type": "core::integer::u32" } ] }, @@ -4814,6 +4857,244 @@ } ] }, + { + "name": "rollyourown::config::ryo_address::ryo_address", + "members": [ + { + "name": "key", + "type": "u8", + "key": true + }, + { + "name": "paper", + "type": "ContractAddress", + "key": false + }, + { + "name": "treasury", + "type": "ContractAddress", + "key": false + } + ], + "class_hash": "0x506d2335400c6ce9cb6d38fe85a3d50542231944837a1fe1b1d32babf35109d", + "abi": [ + { + "type": "impl", + "name": "DojoModelImpl", + "interface_name": "dojo::model::IDojoModel" + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::>", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::>" + } + ] + }, + { + "type": "struct", + "name": "dojo::database::introspect::Struct", + "members": [ + { + "name": "name", + "type": "core::felt252" + }, + { + "name": "attrs", + "type": "core::array::Span::" + }, + { + "name": "children", + "type": "core::array::Span::>" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::<(core::felt252, core::array::Span::)>", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::<(core::felt252, core::array::Span::)>" + } + ] + }, + { + "type": "struct", + "name": "dojo::database::introspect::Enum", + "members": [ + { + "name": "name", + "type": "core::felt252" + }, + { + "name": "attrs", + "type": "core::array::Span::" + }, + { + "name": "children", + "type": "core::array::Span::<(core::felt252, core::array::Span::)>" + } + ] + }, + { + "type": "enum", + "name": "dojo::database::introspect::Ty", + "variants": [ + { + "name": "Primitive", + "type": "core::felt252" + }, + { + "name": "Struct", + "type": "dojo::database::introspect::Struct" + }, + { + "name": "Enum", + "type": "dojo::database::introspect::Enum" + }, + { + "name": "Tuple", + "type": "core::array::Span::>" + }, + { + "name": "Array", + "type": "core::integer::u32" + } + ] + }, + { + "type": "interface", + "name": "dojo::model::IDojoModel", + "items": [ + { + "type": "function", + "name": "name", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "unpacked_size", + "inputs": [], + "outputs": [ + { + "type": "core::integer::u32" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "packed_size", + "inputs": [], + "outputs": [ + { + "type": "core::integer::u32" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "layout", + "inputs": [], + "outputs": [ + { + "type": "core::array::Span::" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "schema", + "inputs": [], + "outputs": [ + { + "type": "dojo::database::introspect::Ty" + } + ], + "state_mutability": "view" + } + ] + }, + { + "type": "impl", + "name": "ryo_addressImpl", + "interface_name": "rollyourown::config::ryo_address::Iryo_address" + }, + { + "type": "struct", + "name": "rollyourown::config::ryo_address::RyoAddress", + "members": [ + { + "name": "key", + "type": "core::integer::u8" + }, + { + "name": "paper", + "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "treasury", + "type": "core::starknet::contract_address::ContractAddress" + } + ] + }, + { + "type": "interface", + "name": "rollyourown::config::ryo_address::Iryo_address", + "items": [ + { + "type": "function", + "name": "ensure_abi", + "inputs": [ + { + "name": "model", + "type": "rollyourown::config::ryo_address::RyoAddress" + } + ], + "outputs": [], + "state_mutability": "view" + } + ] + }, + { + "type": "event", + "name": "rollyourown::config::ryo_address::ryo_address::Event", + "kind": "enum", + "variants": [] + } + ] + }, { "name": "rollyourown::models::game::game", "members": [ @@ -5421,7 +5702,7 @@ }, { "name": "paper_balance", - "type": "u256", + "type": "u32", "key": false }, { @@ -5430,7 +5711,7 @@ "key": false } ], - "class_hash": "0x40a08497ad86f4559941dd6e4c86ad72382b7fd209d019e85c332e9d22ad30", + "class_hash": "0x1920fcf6fc057a3fa3e3a1213bff7325d48e4b3eef6d522d068ffc32a387fc8", "abi": [ { "type": "impl", @@ -5605,20 +5886,6 @@ "name": "leaderboardImpl", "interface_name": "rollyourown::models::leaderboard::Ileaderboard" }, - { - "type": "struct", - "name": "core::integer::u256", - "members": [ - { - "name": "low", - "type": "core::integer::u128" - }, - { - "name": "high", - "type": "core::integer::u128" - } - ] - }, { "type": "enum", "name": "core::bool", @@ -5659,7 +5926,7 @@ }, { "name": "paper_balance", - "type": "core::integer::u256" + "type": "core::integer::u32" }, { "name": "claimed", diff --git a/web/package.json b/web/package.json index 9d71c3678..5c7090d90 100644 --- a/web/package.json +++ b/web/package.json @@ -43,6 +43,7 @@ "next-pwa": "^5.6.0", "react": "^18.2.0", "react-countdown": "^2.3.5", + "react-data-table-component": "^7.6.2", "react-dom": "^18.2.0", "react-json-view-lite": "^1.2.1", "react-query": "^3.39.2", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 150d7d403..8ab34a2c1 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -80,6 +80,9 @@ dependencies: react-countdown: specifier: ^2.3.5 version: 2.3.5(react-dom@18.2.0)(react@18.2.0) + react-data-table-component: + specifier: ^7.6.2 + version: 7.6.2(react@18.2.0)(styled-components@6.1.8) react-dom: specifier: ^18.2.0 version: 18.2.0(react@18.2.0) @@ -2805,6 +2808,10 @@ packages: react: 18.2.0 dev: false + /@emotion/unitless@0.8.0: + resolution: {integrity: sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==} + dev: false + /@emotion/unitless@0.8.1: resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==} dev: false @@ -4646,6 +4653,10 @@ packages: /@types/scheduler@0.16.8: resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==} + /@types/stylis@4.2.0: + resolution: {integrity: sha512-n4sx2bqL0mW1tvDf/loQ+aMX7GQD3lc3fkCMC55VFNDu/vBOabO+LTIeXKM14xK0ppk5TUGcWRjiSpIlUpghKw==} + dev: false + /@types/triple-beam@1.3.5: resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} dev: false @@ -5526,6 +5537,10 @@ packages: engines: {node: '>=6'} dev: true + /camelize@1.0.1: + resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} + dev: false + /caniuse-lite@1.0.30001600: resolution: {integrity: sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==} @@ -5859,11 +5874,28 @@ packages: tiny-invariant: 1.3.1 dev: false + /css-color-keywords@1.0.0: + resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==} + engines: {node: '>=4'} + dev: false + + /css-to-react-native@3.2.0: + resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==} + dependencies: + camelize: 1.0.1 + css-color-keywords: 1.0.0 + postcss-value-parser: 4.2.0 + dev: false + /cssmin@0.3.2: resolution: {integrity: sha512-bynxGIAJ8ybrnFobjsQotIjA8HFDDgPwbeUWNXXXfR+B4f9kkxdcUyagJoQCSUOfMV+ZZ6bMn8bvbozlCzUGwQ==} hasBin: true dev: false + /csstype@3.1.2: + resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} + dev: false + /csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} @@ -8858,6 +8890,10 @@ packages: engines: {node: '>= 0.4'} dev: false + /postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + dev: false + /postcss@8.4.31: resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} @@ -8991,6 +9027,17 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /react-data-table-component@7.6.2(react@18.2.0)(styled-components@6.1.8): + resolution: {integrity: sha512-nHe7040fmtrJyQr/ieGrTfV0jBflYGK4sLokC6/AFOv3ThjmA9WzKz8Z8/2wMxzRqLU+Rn0CVFg+8+frKLepWQ==} + peerDependencies: + react: '>= 16.8.3' + styled-components: '>= 5.0.0' + dependencies: + deepmerge: 4.3.1 + react: 18.2.0 + styled-components: 6.1.8(react-dom@18.2.0)(react@18.2.0) + dev: false + /react-dom@18.2.0(react@18.2.0): resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} peerDependencies: @@ -9496,6 +9543,10 @@ packages: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} dev: true + /shallowequal@1.1.0: + resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} + dev: false + /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -9809,6 +9860,26 @@ packages: engines: {node: '>=14.16'} dev: true + /styled-components@6.1.8(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-PQ6Dn+QxlWyEGCKDS71NGsXoVLKfE1c3vApkvDYS5KAK+V8fNWGhbSUEo9Gg2iaID2tjLXegEW3bZDUGpofRWw==} + engines: {node: '>= 16'} + peerDependencies: + react: '>= 16.8.0' + react-dom: '>= 16.8.0' + dependencies: + '@emotion/is-prop-valid': 1.2.1 + '@emotion/unitless': 0.8.0 + '@types/stylis': 4.2.0 + css-to-react-native: 3.2.0 + csstype: 3.1.2 + postcss: 8.4.31 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + shallowequal: 1.1.0 + stylis: 4.3.1 + tslib: 2.5.0 + dev: false + /styled-jsx@5.1.1(@babel/core@7.24.3)(react@18.2.0): resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} engines: {node: '>= 12.0.0'} @@ -9831,6 +9902,10 @@ packages: resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} dev: false + /stylis@4.3.1: + resolution: {integrity: sha512-EQepAV+wMsIaGVGX1RECzgrcqRRU/0sYOHkeLsZ3fzHaHXZy4DaOOX0vOlGQdlsjkh3mFHAIlVimpwAs4dslyQ==} + dev: false + /summary@2.1.0: resolution: {integrity: sha512-nMIjMrd5Z2nuB2RZCKJfFMjgS3fygbeyGk9PxPPaJR1RIcyN9yn4A63Isovzm3ZtQuEkLBVgMdPup8UeLH7aQw==} dev: true @@ -10061,6 +10136,10 @@ packages: resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==} dev: true + /tslib@2.5.0: + resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} + dev: false + /tslib@2.5.3: resolution: {integrity: sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==} dev: true diff --git a/web/src/components/layout/Header.tsx b/web/src/components/layout/Header.tsx index 198f2d104..af5b0956d 100644 --- a/web/src/components/layout/Header.tsx +++ b/web/src/components/layout/Header.tsx @@ -51,7 +51,7 @@ export const Header = observer(({ back }: HeaderProps) => { {/* {!game && ( <> - {config?.ryo.paper_address && } + {config?.ryoAddress.paper && } {account && } )} */} diff --git a/web/src/components/pages/admin/DrugTable.tsx b/web/src/components/pages/admin/DrugTable.tsx new file mode 100644 index 000000000..c10a81e47 --- /dev/null +++ b/web/src/components/pages/admin/DrugTable.tsx @@ -0,0 +1,13 @@ +import { useDojoContext } from "@/dojo/hooks"; +import { observer } from "mobx-react-lite"; +import DataTable from "react-data-table-component"; +import { customTableStyles, getTableColumns } from "./tables"; + +const columns = getTableColumns(["drug", "drug_id", "name", "base", "step", "weight"]); + +export const DrugTable = observer(() => { + const { + configStore: { config }, + } = useDojoContext(); + return ; +}); diff --git a/web/src/components/pages/admin/GameLayoutTable.tsx b/web/src/components/pages/admin/GameLayoutTable.tsx new file mode 100644 index 000000000..374365b53 --- /dev/null +++ b/web/src/components/pages/admin/GameLayoutTable.tsx @@ -0,0 +1,29 @@ +import { useDojoContext } from "@/dojo/hooks"; +import { observer } from "mobx-react-lite"; +import DataTable from "react-data-table-component"; +import { customTableStyles } from "./tables"; + +// bigint not supported ... +// const columns = getTableColumns(["name", "idx", "bits"]); + +const columns = [ + { + name: "name", + selector: (row) => row["name"], + }, + { + name: "idx", + selector: (row) => Number(row["idx"]), + }, + { + name: "bits", + selector: (row) => Number(row["bits"]), + }, +] + +export const GameLayoutTable = observer(() => { + const { + configStore: { config }, + } = useDojoContext(); + return ; +}); diff --git a/web/src/components/pages/admin/HustlerItemBaseTable.tsx b/web/src/components/pages/admin/HustlerItemBaseTable.tsx new file mode 100644 index 000000000..dd1599b2c --- /dev/null +++ b/web/src/components/pages/admin/HustlerItemBaseTable.tsx @@ -0,0 +1,13 @@ +import { useDojoContext } from "@/dojo/hooks"; +import { observer } from "mobx-react-lite"; +import DataTable from "react-data-table-component"; +import { customTableStyles, getTableColumns } from "./tables"; + +const columns = getTableColumns(["slot", "id", "slot_id", "name", "initial_tier"]); + +export const HustlerItemBaseTable = observer(() => { + const { + configStore: { config }, + } = useDojoContext(); + return ; +}); diff --git a/web/src/components/pages/admin/HustlerItemTiersTable.tsx b/web/src/components/pages/admin/HustlerItemTiersTable.tsx new file mode 100644 index 000000000..fd43f9619 --- /dev/null +++ b/web/src/components/pages/admin/HustlerItemTiersTable.tsx @@ -0,0 +1,13 @@ +import { useDojoContext } from "@/dojo/hooks"; +import { observer } from "mobx-react-lite"; +import DataTable from "react-data-table-component"; +import { customTableStyles, getTableColumns } from "./tables"; + +const columns = getTableColumns(["slot", "tier", "slot_id", "cost", "stat"]); + +export const HustlerItemTiersTable = observer(() => { + const { + configStore: { config }, + } = useDojoContext(); + return ; +}); diff --git a/web/src/components/pages/admin/PlayerLayoutTable.tsx b/web/src/components/pages/admin/PlayerLayoutTable.tsx new file mode 100644 index 000000000..71ef8ca77 --- /dev/null +++ b/web/src/components/pages/admin/PlayerLayoutTable.tsx @@ -0,0 +1,30 @@ +import { useDojoContext } from "@/dojo/hooks"; +import { observer } from "mobx-react-lite"; +import DataTable from "react-data-table-component"; +import { customTableStyles } from "./tables"; + + +// bigint not supported ... +// const columns = getTableColumns(["name", "idx", "bits"]); + +const columns = [ + { + name: "name", + selector: (row) => row["name"], + }, + { + name: "idx", + selector: (row) => Number(row["idx"]), + }, + { + name: "bits", + selector: (row) => Number(row["bits"]), + }, +] + +export const PlayerLayoutTable = observer(() => { + const { + configStore: { config }, + } = useDojoContext(); + return ; +}); diff --git a/web/src/components/pages/admin/tables.ts b/web/src/components/pages/admin/tables.ts new file mode 100644 index 000000000..7bb551c2e --- /dev/null +++ b/web/src/components/pages/admin/tables.ts @@ -0,0 +1,9 @@ + +export function getTableColumns(fields: string[]) { + return fields.map((field) => { + return { + name: field, + selector: (row) => row[field], + }; + }); +} diff --git a/web/src/components/pages/home/ClaimModal.tsx b/web/src/components/pages/home/ClaimModal.tsx index 2e5fb56a2..95f4b91b3 100644 --- a/web/src/components/pages/home/ClaimModal.tsx +++ b/web/src/components/pages/home/ClaimModal.tsx @@ -2,15 +2,15 @@ import { useSystems } from "@/dojo/hooks"; import { Leaderboard } from "@/generated/graphql"; import { formatCash } from "@/utils/ui"; import { - Button, - Modal, - ModalBody, - ModalContent, - ModalFooter, - ModalHeader, - ModalOverlay, - Text, - VStack, + Button, + Modal, + ModalBody, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, + Text, + VStack, } from "@chakra-ui/react"; import { useAccount } from "@starknet-react/core"; diff --git a/web/src/components/wallet/Connect.tsx b/web/src/components/wallet/Connect.tsx index cb08c770d..efff557a5 100644 --- a/web/src/components/wallet/Connect.tsx +++ b/web/src/components/wallet/Connect.tsx @@ -125,7 +125,7 @@ const AccountModal = ({ - + PAPER diff --git a/web/src/components/wallet/PaperFaucet.tsx b/web/src/components/wallet/PaperFaucet.tsx index 0ede3fc92..128d3c805 100644 --- a/web/src/components/wallet/PaperFaucet.tsx +++ b/web/src/components/wallet/PaperFaucet.tsx @@ -11,7 +11,7 @@ const iconsBySymbol = { export const PaperFaucet = () => { const { config } = useConfigStore(); - const { isPending, faucet } = useFaucet(config?.ryo.paper_address); + const { isPending, faucet } = useFaucet(config?.ryoAddress.paper); const onClick = () => { if (isPending) return; diff --git a/web/src/dojo/hooks/useHallOfFame.tsx b/web/src/dojo/hooks/useHallOfFame.tsx index e50dadce6..6d64c2cbd 100644 --- a/web/src/dojo/hooks/useHallOfFame.tsx +++ b/web/src/dojo/hooks/useHallOfFame.tsx @@ -1,5 +1,4 @@ import { Leaderboard, World__ModelEdge, useHallOfFameQuery } from "@/generated/graphql"; -import { formatEther } from "@/utils/ui"; import { useEffect, useMemo } from "react"; import { useDojoContext } from "./useDojoContext"; @@ -7,12 +6,10 @@ export type HallOfFameResult = ReturnType; export const useHallOfFame = () => { const { - chains: { - selectedChain - }, + chains: { selectedChain }, } = useDojoContext(); - const { data, isFetching, isRefetching, isError, refetch } = useHallOfFameQuery({}); + const { data, isFetching, isRefetching, isError, refetch } = useHallOfFameQuery({}); useEffect(() => { refetch(); @@ -24,15 +21,8 @@ export const useHallOfFame = () => { const edges = data.leaderboardModels?.edges as World__ModelEdge[]; const nodes = edges.map((i: World__ModelEdge) => i.node as Leaderboard); - return nodes - .map((i: Leaderboard) => { - return { - ...i, - paper_balance: formatEther(i.paper_balance), - } as Leaderboard; - }) - .sort((a, b) => a.version - b.version); - }, [data, isFetching,isRefetching, isError]); + return nodes.sort((a, b) => a.version - b.version); + }, [data, isFetching, isRefetching, isError]); return { hallOfFame, diff --git a/web/src/dojo/hooks/useSystems.ts b/web/src/dojo/hooks/useSystems.ts index 8dd949775..ca38dd25f 100644 --- a/web/src/dojo/hooks/useSystems.ts +++ b/web/src/dojo/hooks/useSystems.ts @@ -20,9 +20,13 @@ export interface SystemsInterface { endGame: (gameId: string, actions: Array) => Promise; decide: (gameId: string, action: EncountersAction) => Promise; claim: (season: number) => Promise; - +// + claimTreasury: () => Promise; + setPaperFee: (fee: number) => Promise; + setTreasuryFeePct: (fee: number) => Promise; +// failingTx: () => Promise; - feedLeaderboard: (count: number) => Promise; + feedLeaderboard: (count: number) => Promise; isPending: boolean; error?: string; @@ -185,7 +189,7 @@ export const useSystems = (): SystemsInterface => { async (gameMode: GameMode, hustlerId: number, playerName: string,) => { const paperFee = config?.ryo.paper_fee * 10 ** 18; - const paperAddress = config?.ryo.paper_address; + const paperAddress = config?.ryoAddress.paper; const gameAddress = dojoProvider.manifest.contracts.find((i: any) => i.name === "rollyourown::systems::game::game").address; // const approvalCall: Call = { @@ -211,7 +215,7 @@ export const useSystems = (): SystemsInterface => { gameId: gameCreated.gameId, }; }, - [executeAndReceipt, config?.ryo.paper_address], + [executeAndReceipt, config?.ryoAddress.paper], ); @@ -324,6 +328,95 @@ export const useSystems = (): SystemsInterface => { + // + // + // + + + const claimTreasury = useCallback( + async () => { + + const { hash, events, parsedEvents } = await executeAndReceipt( + { + contractName: "rollyourown::systems::game::game", + functionName: "claim_treasury", + callData: [], + } + ); + + return { + hash, + }; + + }, + [executeAndReceipt], + ); + + + + const setPaused = useCallback( + async (paused: boolean) => { + + const { hash, events, parsedEvents } = await executeAndReceipt( + { + contractName: "rollyourown::systems::ryo::ryo", + functionName: "set_paused", + callData: [paused], + } + ); + + return { + hash, + }; + + }, + [executeAndReceipt], + ); + + + const setPaperFee = useCallback( + async (paperFee: number) => { + + const { hash, events, parsedEvents } = await executeAndReceipt( + { + contractName: "rollyourown::systems::ryo::ryo", + functionName: "set_paper_fee", + callData: [paperFee], + } + ); + + return { + hash, + }; + + }, + [executeAndReceipt], + ); + + const setTreasuryFeePct = useCallback( + async (treasuryFeePct: number) => { + + const { hash, events, parsedEvents } = await executeAndReceipt( + { + contractName: "rollyourown::systems::ryo::ryo", + functionName: "set_treasury_fee_pct", + callData: [treasuryFeePct], + } + ); + + return { + hash, + }; + + }, + [executeAndReceipt], + ); + + + + + + // // // @@ -373,6 +466,11 @@ export const useSystems = (): SystemsInterface => { endGame, decide, claim, + claimTreasury, + // + setPaused, + setPaperFee, + setTreasuryFeePct, // feedLeaderboard, failingTx, diff --git a/web/src/dojo/setup/config.ts b/web/src/dojo/setup/config.ts index 3abb5b878..36425b744 100644 --- a/web/src/dojo/setup/config.ts +++ b/web/src/dojo/setup/config.ts @@ -24,7 +24,7 @@ const katanaLocal: DojoChainConfig = { name: "KATANA", chainConfig: katanaLocalChain, rpcUrl: process.env.NEXT_PUBLIC_RPC_ENDPOINT || "http://localhost:5050", - toriiUrl: process.env.NEXT_PUBLIC_GRAPHQL_ENDPOINT || "http://localhost:8080", + toriiUrl: process.env.NEXT_PUBLIC_GRAPHQL_ENDPOINT || "http://localhost:8080/graphql", toriiWsUrl: process.env.NEXT_PUBLIC_GRAPHQL_ENDPOINT_WS || "ws://localhost:8080/graphql/ws", masterAddress: process.env.NEXT_PUBLIC_ADMIN_ADDRESS || diff --git a/web/src/dojo/stores/config.tsx b/web/src/dojo/stores/config.tsx index 6310adfe6..3ca1ddd12 100644 --- a/web/src/dojo/stores/config.tsx +++ b/web/src/dojo/stores/config.tsx @@ -9,6 +9,8 @@ import { HustlerItemTiersConfigEdge, LocationConfig, LocationConfigEdge, + RyoAddress, + RyoAddressEdge, RyoConfig, RyoConfigEdge, } from "@/generated/graphql"; @@ -68,6 +70,7 @@ export type GetConfig = { export type Config = { ryo: RyoConfig; + ryoAddress: RyoAddress; drug: DrugConfigFull[]; location: LocationConfigFull[]; items: HustlerItemBaseConfig[]; @@ -117,6 +120,9 @@ export class ConfigStoreClass { const ryoConfigEdges = data.ryoConfigModels!.edges as RyoConfigEdge[]; const ryoConfig = ryoConfigEdges[0]!.node as RyoConfig; + const ryoAddressEdges = data.ryoAddressModels!.edges as RyoAddressEdge[]; + const ryoAddress = ryoAddressEdges[0]!.node as RyoAddress; + /*************************************************** */ const drugConfigEdges = data.drugConfigModels!.edges as DrugConfigEdge[]; @@ -182,6 +188,7 @@ export class ConfigStoreClass { this.config = { ryo: ryoConfig, + ryoAddress: ryoAddress, drug: drugConfigFull, location: locationConfigFull, items: hustlerItemBaseConfig, @@ -191,7 +198,7 @@ export class ConfigStoreClass { }; } catch (e: any) { console.log("ERROR: ConfigStoreClass.init"); - // console.log(e); + console.log(e); } this.isLoading = false; @@ -202,6 +209,7 @@ export class ConfigStoreClass { getDrug(drug: string): DrugConfigFull { return this.config?.drug.find((i) => i.drug.toLowerCase() === drug.toLowerCase())!; } + getDrugById(drug_id: number): DrugConfigFull { return this.config?.drug.find((i) => Number(i.drug_id) === Number(drug_id))!; } @@ -214,6 +222,8 @@ export class ConfigStoreClass { return this.config?.location.find((i) => Number(i.location_id) === Number(location_id))!; } + // layout + getGameStoreLayoutItem(name: string): LayoutItem { // return this.config?.config.layouts.game_store.find((i) => shortString.decodeShortString(i.name) === name)!; return this.config?.config.layouts.game_store.find((i) => i.name === name)!; @@ -223,6 +233,8 @@ export class ConfigStoreClass { return this.config?.config.layouts.player.find((i) => i.name === name)!; } + // hustlers + getHustlerById(id: number): HustlerConfig { return this.config?.config.hustlers.find((i) => Number(i.hustler_id) === Number(id))!; } diff --git a/web/src/generated/graphql.ts b/web/src/generated/graphql.ts index 0d7597c7d..95131a607 100644 --- a/web/src/generated/graphql.ts +++ b/web/src/generated/graphql.ts @@ -728,7 +728,7 @@ export type Leaderboard = { game_id?: Maybe; high_score?: Maybe; next_version_timestamp?: Maybe; - paper_balance?: Maybe; + paper_balance?: Maybe; player_id?: Maybe; version?: Maybe; }; @@ -784,13 +784,13 @@ export type LeaderboardWhereInput = { next_version_timestampLT?: InputMaybe; next_version_timestampLTE?: InputMaybe; next_version_timestampNEQ?: InputMaybe; - paper_balance?: InputMaybe; - paper_balanceEQ?: InputMaybe; - paper_balanceGT?: InputMaybe; - paper_balanceGTE?: InputMaybe; - paper_balanceLT?: InputMaybe; - paper_balanceLTE?: InputMaybe; - paper_balanceNEQ?: InputMaybe; + paper_balance?: InputMaybe; + paper_balanceEQ?: InputMaybe; + paper_balanceGT?: InputMaybe; + paper_balanceGTE?: InputMaybe; + paper_balanceLT?: InputMaybe; + paper_balanceLTE?: InputMaybe; + paper_balanceNEQ?: InputMaybe; player_id?: InputMaybe; player_idEQ?: InputMaybe; player_idGT?: InputMaybe; @@ -857,13 +857,69 @@ export type LocationConfigWhereInput = { nameNEQ?: InputMaybe; }; -export type ModelUnion = DrugConfig | Erc20AllowanceModel | Erc20BalanceModel | Erc20MetadataModel | Game | GameConfig | GameStorePacked | HustlerItemBaseConfig | HustlerItemTiersConfig | InitializableModel | Leaderboard | LocationConfig | RyoConfig; +export type ModelUnion = DrugConfig | Erc20AllowanceModel | Erc20BalanceModel | Erc20MetadataModel | Game | GameConfig | GameStorePacked | HustlerItemBaseConfig | HustlerItemTiersConfig | InitializableModel | Leaderboard | LocationConfig | RyoAddress | RyoConfig; export enum OrderDirection { Asc = 'ASC', Desc = 'DESC' } +export type RyoAddress = { + __typename?: 'RyoAddress'; + entity?: Maybe; + key?: Maybe; + paper?: Maybe; + treasury?: Maybe; +}; + +export type RyoAddressConnection = { + __typename?: 'RyoAddressConnection'; + edges?: Maybe>>; + pageInfo: World__PageInfo; + totalCount: Scalars['Int']; +}; + +export type RyoAddressEdge = { + __typename?: 'RyoAddressEdge'; + cursor?: Maybe; + node?: Maybe; +}; + +export type RyoAddressOrder = { + direction: OrderDirection; + field: RyoAddressOrderField; +}; + +export enum RyoAddressOrderField { + Key = 'KEY', + Paper = 'PAPER', + Treasury = 'TREASURY' +} + +export type RyoAddressWhereInput = { + key?: InputMaybe; + keyEQ?: InputMaybe; + keyGT?: InputMaybe; + keyGTE?: InputMaybe; + keyLT?: InputMaybe; + keyLTE?: InputMaybe; + keyNEQ?: InputMaybe; + paper?: InputMaybe; + paperEQ?: InputMaybe; + paperGT?: InputMaybe; + paperGTE?: InputMaybe; + paperLT?: InputMaybe; + paperLTE?: InputMaybe; + paperNEQ?: InputMaybe; + treasury?: InputMaybe; + treasuryEQ?: InputMaybe; + treasuryGT?: InputMaybe; + treasuryGTE?: InputMaybe; + treasuryLT?: InputMaybe; + treasuryLTE?: InputMaybe; + treasuryNEQ?: InputMaybe; +}; + export type RyoConfig = { __typename?: 'RyoConfig'; entity?: Maybe; @@ -871,9 +927,10 @@ export type RyoConfig = { key?: Maybe; leaderboard_duration?: Maybe; leaderboard_version?: Maybe; - paper_address?: Maybe; paper_fee?: Maybe; paused?: Maybe; + treasury_balance?: Maybe; + treasury_fee_pct?: Maybe; }; export type RyoConfigConnection = { @@ -899,9 +956,10 @@ export enum RyoConfigOrderField { Key = 'KEY', LeaderboardDuration = 'LEADERBOARD_DURATION', LeaderboardVersion = 'LEADERBOARD_VERSION', - PaperAddress = 'PAPER_ADDRESS', PaperFee = 'PAPER_FEE', - Paused = 'PAUSED' + Paused = 'PAUSED', + TreasuryBalance = 'TREASURY_BALANCE', + TreasuryFeePct = 'TREASURY_FEE_PCT' } export type RyoConfigWhereInput = { @@ -927,13 +985,6 @@ export type RyoConfigWhereInput = { leaderboard_versionLT?: InputMaybe; leaderboard_versionLTE?: InputMaybe; leaderboard_versionNEQ?: InputMaybe; - paper_address?: InputMaybe; - paper_addressEQ?: InputMaybe; - paper_addressGT?: InputMaybe; - paper_addressGTE?: InputMaybe; - paper_addressLT?: InputMaybe; - paper_addressLTE?: InputMaybe; - paper_addressNEQ?: InputMaybe; paper_fee?: InputMaybe; paper_feeEQ?: InputMaybe; paper_feeGT?: InputMaybe; @@ -942,6 +993,20 @@ export type RyoConfigWhereInput = { paper_feeLTE?: InputMaybe; paper_feeNEQ?: InputMaybe; paused?: InputMaybe; + treasury_balance?: InputMaybe; + treasury_balanceEQ?: InputMaybe; + treasury_balanceGT?: InputMaybe; + treasury_balanceGTE?: InputMaybe; + treasury_balanceLT?: InputMaybe; + treasury_balanceLTE?: InputMaybe; + treasury_balanceNEQ?: InputMaybe; + treasury_fee_pct?: InputMaybe; + treasury_fee_pctEQ?: InputMaybe; + treasury_fee_pctGT?: InputMaybe; + treasury_fee_pctGTE?: InputMaybe; + treasury_fee_pctLT?: InputMaybe; + treasury_fee_pctLTE?: InputMaybe; + treasury_fee_pctNEQ?: InputMaybe; }; export type World__Content = { @@ -1085,6 +1150,7 @@ export type World__Query = { metadatas?: Maybe; model: World__Model; models?: Maybe; + ryoAddressModels?: Maybe; ryoConfigModels?: Maybe; transaction: World__Transaction; transactions?: Maybe; @@ -1288,6 +1354,18 @@ export type World__QueryModelsArgs = { }; +export type World__QueryRyoAddressModelsArgs = { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + order?: InputMaybe; + where?: InputMaybe; +}; + + export type World__QueryRyoConfigModelsArgs = { after?: InputMaybe; before?: InputMaybe; @@ -1370,7 +1448,7 @@ export type World__TransactionEdge = { export type ConfigQueryVariables = Exact<{ [key: string]: never; }>; -export type ConfigQuery = { __typename?: 'World__Query', ryoConfigModels?: { __typename?: 'RyoConfigConnection', edges?: Array<{ __typename?: 'RyoConfigEdge', node?: { __typename?: 'RyoConfig', key?: any | null, initialized?: any | null, paused?: any | null, leaderboard_version?: any | null, paper_address?: any | null, paper_fee?: any | null } | null } | null> | null } | null, drugConfigModels?: { __typename?: 'DrugConfigConnection', edges?: Array<{ __typename?: 'DrugConfigEdge', node?: { __typename?: 'DrugConfig', drug?: any | null, drug_id?: any | null, base?: any | null, step?: any | null, weight?: any | null, name?: any | null } | null } | null> | null } | null, locationConfigModels?: { __typename?: 'LocationConfigConnection', edges?: Array<{ __typename?: 'LocationConfigEdge', node?: { __typename?: 'LocationConfig', location?: any | null, location_id?: any | null, name?: any | null } | null } | null> | null } | null, hustlerItemBaseConfigModels?: { __typename?: 'HustlerItemBaseConfigConnection', edges?: Array<{ __typename?: 'HustlerItemBaseConfigEdge', node?: { __typename?: 'HustlerItemBaseConfig', slot?: any | null, id?: any | null, slot_id?: any | null, name?: any | null, initial_tier?: any | null } | null } | null> | null } | null, hustlerItemTiersConfigModels?: { __typename?: 'HustlerItemTiersConfigConnection', edges?: Array<{ __typename?: 'HustlerItemTiersConfigEdge', node?: { __typename?: 'HustlerItemTiersConfig', slot?: any | null, slot_id?: any | null, tier?: any | null, cost?: any | null, stat?: any | null } | null } | null> | null } | null }; +export type ConfigQuery = { __typename?: 'World__Query', ryoAddressModels?: { __typename?: 'RyoAddressConnection', edges?: Array<{ __typename?: 'RyoAddressEdge', node?: { __typename?: 'RyoAddress', key?: any | null, paper?: any | null, treasury?: any | null } | null } | null> | null } | null, ryoConfigModels?: { __typename?: 'RyoConfigConnection', edges?: Array<{ __typename?: 'RyoConfigEdge', node?: { __typename?: 'RyoConfig', key?: any | null, initialized?: any | null, paused?: any | null, leaderboard_version?: any | null, paper_fee?: any | null, treasury_fee_pct?: any | null, treasury_balance?: any | null } | null } | null> | null } | null, drugConfigModels?: { __typename?: 'DrugConfigConnection', edges?: Array<{ __typename?: 'DrugConfigEdge', node?: { __typename?: 'DrugConfig', drug?: any | null, drug_id?: any | null, base?: any | null, step?: any | null, weight?: any | null, name?: any | null } | null } | null> | null } | null, locationConfigModels?: { __typename?: 'LocationConfigConnection', edges?: Array<{ __typename?: 'LocationConfigEdge', node?: { __typename?: 'LocationConfig', location?: any | null, location_id?: any | null, name?: any | null } | null } | null> | null } | null, hustlerItemBaseConfigModels?: { __typename?: 'HustlerItemBaseConfigConnection', edges?: Array<{ __typename?: 'HustlerItemBaseConfigEdge', node?: { __typename?: 'HustlerItemBaseConfig', slot?: any | null, id?: any | null, slot_id?: any | null, name?: any | null, initial_tier?: any | null } | null } | null> | null } | null, hustlerItemTiersConfigModels?: { __typename?: 'HustlerItemTiersConfigConnection', edges?: Array<{ __typename?: 'HustlerItemTiersConfigEdge', node?: { __typename?: 'HustlerItemTiersConfig', slot?: any | null, slot_id?: any | null, tier?: any | null, cost?: any | null, stat?: any | null } | null } | null> | null } | null }; export type GameEventsQueryVariables = Exact<{ gameId: Scalars['String']; @@ -1399,14 +1477,14 @@ export type GameStorePackedQueryVariables = Exact<{ }>; -export type GameStorePackedQuery = { __typename?: 'World__Query', entities?: { __typename?: 'World__EntityConnection', totalCount: number, edges?: Array<{ __typename?: 'World__EntityEdge', node?: { __typename?: 'World__Entity', id?: string | null, models?: Array<{ __typename: 'DrugConfig' } | { __typename: 'ERC20AllowanceModel' } | { __typename: 'ERC20BalanceModel' } | { __typename: 'ERC20MetadataModel' } | { __typename: 'Game' } | { __typename: 'GameConfig' } | { __typename: 'GameStorePacked', game_id?: any | null, player_id?: any | null, packed?: any | null } | { __typename: 'HustlerItemBaseConfig' } | { __typename: 'HustlerItemTiersConfig' } | { __typename: 'InitializableModel' } | { __typename: 'Leaderboard' } | { __typename: 'LocationConfig' } | { __typename: 'RyoConfig' } | null> | null } | null } | null> | null } | null }; +export type GameStorePackedQuery = { __typename?: 'World__Query', entities?: { __typename?: 'World__EntityConnection', totalCount: number, edges?: Array<{ __typename?: 'World__EntityEdge', node?: { __typename?: 'World__Entity', id?: string | null, models?: Array<{ __typename: 'DrugConfig' } | { __typename: 'ERC20AllowanceModel' } | { __typename: 'ERC20BalanceModel' } | { __typename: 'ERC20MetadataModel' } | { __typename: 'Game' } | { __typename: 'GameConfig' } | { __typename: 'GameStorePacked', game_id?: any | null, player_id?: any | null, packed?: any | null } | { __typename: 'HustlerItemBaseConfig' } | { __typename: 'HustlerItemTiersConfig' } | { __typename: 'InitializableModel' } | { __typename: 'Leaderboard' } | { __typename: 'LocationConfig' } | { __typename: 'RyoAddress' } | { __typename: 'RyoConfig' } | null> | null } | null } | null> | null } | null }; export type GameStorePackedSubscriptionSubscriptionVariables = Exact<{ id?: InputMaybe; }>; -export type GameStorePackedSubscriptionSubscription = { __typename?: 'World__Subscription', entityUpdated: { __typename?: 'World__Entity', id?: string | null, keys?: Array | null, models?: Array<{ __typename: 'DrugConfig' } | { __typename: 'ERC20AllowanceModel' } | { __typename: 'ERC20BalanceModel' } | { __typename: 'ERC20MetadataModel' } | { __typename: 'Game' } | { __typename: 'GameConfig' } | { __typename: 'GameStorePacked', game_id?: any | null, player_id?: any | null, packed?: any | null } | { __typename: 'HustlerItemBaseConfig' } | { __typename: 'HustlerItemTiersConfig' } | { __typename: 'InitializableModel' } | { __typename: 'Leaderboard' } | { __typename: 'LocationConfig' } | { __typename: 'RyoConfig' } | null> | null } }; +export type GameStorePackedSubscriptionSubscription = { __typename?: 'World__Subscription', entityUpdated: { __typename?: 'World__Entity', id?: string | null, keys?: Array | null, models?: Array<{ __typename: 'DrugConfig' } | { __typename: 'ERC20AllowanceModel' } | { __typename: 'ERC20BalanceModel' } | { __typename: 'ERC20MetadataModel' } | { __typename: 'Game' } | { __typename: 'GameConfig' } | { __typename: 'GameStorePacked', game_id?: any | null, player_id?: any | null, packed?: any | null } | { __typename: 'HustlerItemBaseConfig' } | { __typename: 'HustlerItemTiersConfig' } | { __typename: 'InitializableModel' } | { __typename: 'Leaderboard' } | { __typename: 'LocationConfig' } | { __typename: 'RyoAddress' } | { __typename: 'RyoConfig' } | null> | null } }; export type LeaderboardByVersionQueryVariables = Exact<{ version?: InputMaybe; @@ -1431,6 +1509,15 @@ export type GameOverEventsQuery = { __typename?: 'World__Query', events?: { __ty export const ConfigDocument = ` query Config { + ryoAddressModels(limit: 1) { + edges { + node { + key + paper + treasury + } + } + } ryoConfigModels(limit: 1) { edges { node { @@ -1438,8 +1525,9 @@ export const ConfigDocument = ` initialized paused leaderboard_version - paper_address paper_fee + treasury_fee_pct + treasury_balance } } } diff --git a/web/src/generated/introspection.ts b/web/src/generated/introspection.ts index a3316b606..7dc983d2b 100644 --- a/web/src/generated/introspection.ts +++ b/web/src/generated/introspection.ts @@ -19,6 +19,7 @@ "InitializableModel", "Leaderboard", "LocationConfig", + "RyoAddress", "RyoConfig" ] } diff --git a/web/src/graphql/config.graphql b/web/src/graphql/config.graphql index b25321738..73552ff75 100644 --- a/web/src/graphql/config.graphql +++ b/web/src/graphql/config.graphql @@ -1,4 +1,16 @@ query Config { + + # RyoAddress + ryoAddressModels(limit:1){ + edges { + node { + key, + paper, + treasury, + } + } + } + # Ryo ryoConfigModels(limit:1){ edges { @@ -7,8 +19,9 @@ query Config { initialized, paused, leaderboard_version, - paper_address, paper_fee, + treasury_fee_pct, + treasury_balance, } } } diff --git a/web/src/pages/admin.tsx b/web/src/pages/admin.tsx new file mode 100644 index 000000000..e963f536c --- /dev/null +++ b/web/src/pages/admin.tsx @@ -0,0 +1,262 @@ +import { Layout } from "@/components/layout"; +import { ChildrenOrConnect, TokenBalance } from "@/components/wallet"; +import { useDojoContext, useRouterContext, useSystems } from "@/dojo/hooks"; +import { + Button, + Card, + CardBody, + CardFooter, + CardHeader, + Flex, + HStack, + Input, + Tab, + TabList, + TabPanel, + TabPanels, + Tabs, + Text, + VStack +} from "@chakra-ui/react"; +import { useAccount } from "@starknet-react/core"; +import { observer } from "mobx-react-lite"; + +import { Wallet } from "@/components/icons/archive"; +import { DrugTable } from "@/components/pages/admin/DrugTable"; +import { GameLayoutTable } from "@/components/pages/admin/GameLayoutTable"; +import { HustlerItemBaseTable } from "@/components/pages/admin/HustlerItemBaseTable"; +import { HustlerItemTiersTable } from "@/components/pages/admin/HustlerItemTiersTable"; +import { PlayerLayoutTable } from "@/components/pages/admin/PlayerLayoutTable"; +import { useEffect, useState } from "react"; + +export default function Admin() { + const { router } = useRouterContext(); + const { account } = useAccount(); + + return ( + + + + ADMIN + DRUGS + ITEMS + LAYOUTS + + + + + + + + + + + + + + + + + + + + + ITEM BASE + + + + ITEM TIERS + + + + + + + + + GAME + + + + PLAYER + + + + + + + + + ); +} + +const RyoAddressCard = observer(() => { + const { + configStore: { config }, + } = useDojoContext(); + + return ( + + + RYO ADDRESS + + + + + PAPER + {config?.ryoAddress.paper} + + + TREASURY + {config?.ryoAddress.treasury} + + + + + ); +}); + +const TreasuryClaimCard = observer(() => { + const { configStore } = useDojoContext(); + const { config } = configStore; + + const { claimTreasury, isPending } = useSystems(); + + const onClaim = async () => { + await claimTreasury(); + await configStore.init(); + }; + + return ( + + + TREASURY + + + + + PAPER BALANCE + + + + PAPER CLAIMABLE + {config?.ryo.treasury_balance} + + + + + + + + + + ); +}); + +const RyoPauseCard = observer(() => { + const { configStore } = useDojoContext(); + const { config } = configStore; + + const { setPaused, isPending } = useSystems(); + + const onTogglePause = async () => { + await setPaused(!config?.ryo.paused); + await configStore.init(); + }; + + return ( + + + RYO + + + + {config?.ryo.paused ? "PAUSED" : "NOT PAUSED"} + + + + + + + + + ); +}); + +const RyoFeeCard = observer(() => { + const { configStore } = useDojoContext(); + const { config } = configStore; + + const [paperFeeValue, setPaperFeeValue] = useState(config?.ryo.paper_fee); + const [treasuryFeePctValue, setTreasuryFeePctValue] = useState(config?.ryo.treasury_fee_pct); + + const { setPaperFee, setTreasuryFeePct, isPending } = useSystems(); + + useEffect(() => { + setPaperFeeValue(config?.ryo.paper_fee); + }, [config?.ryo.paper_fee]); + + useEffect(() => { + setTreasuryFeePctValue(config?.ryo.treasury_fee_pct); + }, [config?.ryo.treasury_fee_pct]); + + // const { setPaused, isPending } = useSystems(); + + const updatePaperFee = async () => { + await setPaperFee(paperFeeValue); + await configStore.init(); + }; + + const updateTreasuryFeePct = async () => { + await setTreasuryFeePct(treasuryFeePctValue); + await configStore.init(); + }; + + return ( + + + RYO FEES + + + + + + PAPER FEE + + { + setPaperFeeValue(e.target.value); + }} + /> + + + + + + TREASURY FEE % + + { + setTreasuryFeePctValue(e.target.value); + }} + /> + + + + + + + ); +}); diff --git a/web/src/pages/create/new.tsx b/web/src/pages/create/new.tsx index e25761a64..7b3477495 100644 --- a/web/src/pages/create/new.tsx +++ b/web/src/pages/create/new.tsx @@ -227,7 +227,7 @@ const New = observer(() => { YOU OWN - + PAPER diff --git a/web/src/theme/global.tsx b/web/src/theme/global.tsx index 2e7b0c59b..d23a71580 100644 --- a/web/src/theme/global.tsx +++ b/web/src/theme/global.tsx @@ -1,11 +1,36 @@ import { Global } from "@emotion/react"; +import colors from "./colors"; + const GlobalStyles = () => ( );