Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: starkbank for starkpimps at HH #107

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/components.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ mod drug;
mod location;
mod risks;
mod name;
mod bank;
6 changes: 6 additions & 0 deletions src/components/bank.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#[derive(Component, Copy, Drop, Serde, SerdeLen)]
struct Bank {
amount: u128,
}


1 change: 1 addition & 0 deletions src/constants.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ const MIN_CASH: u128 = 1_000_000_000; // $100k

// cash players start with
const STARTING_CASH: u128 = 20_000_000; // $2000
const STARTING_HEALTH: u8 = 100;
15 changes: 15 additions & 0 deletions src/events.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,18 @@ struct RandomEvent {
mugged: bool,
arrested: bool
}


#[derive(Drop, Serde)]
struct BankDeposit {
game_id: u32,
player_id: felt252,
quantity: u128,
}

#[derive(Drop, Serde)]
struct BankWithdraw {
game_id: u32,
player_id: felt252,
quantity: u128,
}
1 change: 1 addition & 0 deletions src/systems.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ mod join;
mod trade;
mod travel;
mod player;
mod bank;
104 changes: 104 additions & 0 deletions src/systems/bank.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#[system]
mod bank_deposit {
use traits::Into;
use array::ArrayTrait;
use debug::PrintTrait;
use serde::Serde;

use dojo::world::Context;

use rollyourown::components::name::Name;
use rollyourown::components::player::Player;
use rollyourown::components::game::{Game, GameTrait};
use rollyourown::components::bank::{Bank};
use rollyourown::events::{emit, BankDeposit};


// 1. Verify the caller owns the player.
// 2. Ensure user have enough money
// 3. Update player's cash
// 4. Update bank cash
fn execute(ctx: Context, game_id: u32, quantity: u128) {
let game = get !(ctx.world, game_id.into(), Game);
assert(game.tick(), 'cannot progress');

let player_id = ctx.origin.into();
let player_key = (game_id, player_id).into();
let (player, bank) = get !(ctx.world, player_key, (Player, Bank));

assert(quantity <= player.cash, 'not enough cash');

// update bank
set !(ctx.world, player_key, (Bank { amount: bank.amount + quantity }));

// update player
set !(
ctx.world,
player_key,
(Player {
cash: player.cash - quantity,
health: player.health,
turns_remaining: player.turns_remaining,
})
);

// event
let mut event_values = ArrayTrait::new();
Serde::serialize(@BankDeposit { game_id, player_id, quantity: quantity }, ref event_values);
emit(ctx, 'BankDeposit', event_values.span());
}
}


#[system]
mod bank_withdraw {
use traits::Into;
use array::ArrayTrait;
use debug::PrintTrait;
use serde::Serde;

use dojo::world::Context;

use rollyourown::components::name::Name;
use rollyourown::components::player::Player;
use rollyourown::components::game::{Game, GameTrait};
use rollyourown::components::bank::{Bank};
use rollyourown::events::{emit, BankWithdraw};


// 1. Verify the caller owns the player.
// 2. Ensure can withdraw money
// 3. Update player's cash
// 4. Update bank cash
fn execute(ctx: Context, game_id: u32, quantity: u128) {
let game = get !(ctx.world, game_id.into(), Game);
assert(game.tick(), 'cannot progress');

let player_id = ctx.origin.into();
let player_key = (game_id, player_id).into();
let (player, bank) = get !(ctx.world, player_key, (Player, Bank));

assert(bank.amount >= quantity, 'not enough cash');

// update bank
set !(ctx.world, player_key, (Bank { amount: bank.amount - quantity }));

// update player
set !(
ctx.world,
player_key,
(Player {
cash: player.cash + quantity,
health: player.health,
turns_remaining: player.turns_remaining,
})
);

// event
let mut event_values = ArrayTrait::new();
Serde::serialize(
@BankWithdraw { game_id, player_id, quantity: quantity }, ref event_values
);
emit(ctx, 'BankWithdraw', event_values.span());
}
}
7 changes: 5 additions & 2 deletions src/systems/create.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ mod create_game {
use rollyourown::components::risks::Risks;
use rollyourown::components::market::Market;
use rollyourown::components::drug::{Drug, DrugTrait};
use rollyourown::components::bank::{Bank};
use rollyourown::components::location::{Location, LocationTrait};
use rollyourown::constants::{
SCALING_FACTOR, TRAVEL_RISK, HURT_RISK, MUGGED_RISK, ARRESTED_RISK, MIN_CASH, MAX_CASH,
MIN_QUANITTY, MAX_QUANTITY, STARTING_CASH
MIN_QUANITTY, MAX_QUANTITY, STARTING_CASH, STARTING_HEALTH
};
use rollyourown::utils::random;

Expand Down Expand Up @@ -50,9 +51,11 @@ mod create_game {
(game_id, player_id).into(),
(
Player {
cash: STARTING_CASH, health: 100, turns_remaining: max_turns
cash: STARTING_CASH, health: STARTING_HEALTH, turns_remaining: max_turns
}, Location {
name: location_name
}, Bank {
amount: 0
}
)
);
Expand Down
8 changes: 6 additions & 2 deletions src/systems/join.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ mod join_game {
use rollyourown::events::{emit, PlayerJoined};
use rollyourown::components::game::Game;
use rollyourown::components::player::Player;
use rollyourown::components::bank::{Bank};
use rollyourown::components::location::{Location, LocationTrait};
use rollyourown::constants::{SCALING_FACTOR, STARTING_CASH};
use rollyourown::constants::{SCALING_FACTOR, STARTING_CASH, STARTING_HEALTH};

fn execute(ctx: Context, game_id: u32) -> felt252 {
let block_info = starknet::get_block_info().unbox();
Expand All @@ -21,6 +22,7 @@ mod join_game {
assert(!game.is_finished, 'game is finished');
assert(game.max_players > game.num_players, 'game is full');
assert(game.start_time >= block_info.block_timestamp, 'already started');
// TODO : check that player can't create/join multiple times ?

let seed = starknet::get_tx_info().unbox().transaction_hash;
let location_name = LocationTrait::random(seed);
Expand All @@ -30,9 +32,11 @@ mod join_game {
(game_id, player_id).into(),
(
Player {
cash: STARTING_CASH, health: 100, turns_remaining: game.max_turns
cash: STARTING_CASH, health: STARTING_HEALTH, turns_remaining: game.max_turns
}, Location {
name: location_name
}, Bank {
amount: 0
}
)
);
Expand Down
1 change: 1 addition & 0 deletions src/tests.cairo
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod create;
mod travel;
mod player;
mod bank;
//mod trade;


114 changes: 114 additions & 0 deletions src/tests/bank.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
use core::{traits::Into, result::ResultTrait};
use array::{ArrayTrait, SpanTrait};
use option::OptionTrait;
use traits::TryInto;
use box::BoxTrait;
use debug::PrintTrait;

use starknet::{ContractAddress, syscalls::deploy_syscall};
use starknet::class_hash::{ClassHash, Felt252TryIntoClassHash};
use starknet::contract_address_const;
use dojo::database::query::{IntoPartitioned, IntoPartitionedQuery};
use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait};
use dojo::world::Context;
use dojo::SerdeLen;
use serde::Serde;

use rollyourown::components::{player::Player, bank::Bank};
use rollyourown::tests::create::{spawn_game, spawn_player};
use rollyourown::constants::{SCALING_FACTOR, STARTING_CASH};


#[test]
#[available_gas(100000000)]
#[should_panic(
expected: ('not enough cash', 'ENTRYPOINT_FAILED', 'ENTRYPOINT_FAILED', 'ENTRYPOINT_FAILED')
)]
fn test_bank_deposit_more_than_owned() {
let (world_address, game_id, player_id) = spawn_game();
let world = IWorldDispatcher { contract_address: world_address };

let p1_key = (game_id, player_id);

// get player
let mut p1_ser = world.entity('Player'.into(), p1_key.into(), 0, SerdeLen::<Player>::len());
let p1 = Serde::<Player>::deserialize(ref p1_ser).expect('player not found');

let mut bank_calldata = ArrayTrait::new();
bank_calldata.append(game_id.into());
bank_calldata.append((STARTING_CASH + 1 * SCALING_FACTOR).into());

//call deposit
let res_deposit = world.execute('bank_deposit'.into(), bank_calldata.span());
}

#[test]
#[available_gas(100000000)]
#[should_panic(
expected: ('not enough cash', 'ENTRYPOINT_FAILED', 'ENTRYPOINT_FAILED', 'ENTRYPOINT_FAILED')
)]
fn test_bank_withdraw_more_than_deposited() {
let (world_address, game_id, player_id) = spawn_game();
let world = IWorldDispatcher { contract_address: world_address };

let p1_key = (game_id, player_id);

// get player
let mut p1_ser = world.entity('Player'.into(), p1_key.into(), 0, SerdeLen::<Player>::len());
let p1 = Serde::<Player>::deserialize(ref p1_ser).expect('player not found');

let mut bank_calldata = ArrayTrait::new();
bank_calldata.append(game_id.into());
bank_calldata.append((1 * SCALING_FACTOR).into());

//call deposit
let res_deposit = world.execute('bank_withdraw'.into(), bank_calldata.span());
}


#[test]
#[available_gas(100000000)]
fn test_multiplayer() {
let (world_address, game_id, player1_id) = spawn_game();
let world = IWorldDispatcher { contract_address: world_address };

let p1_key = (game_id, player1_id);

// get player
let mut p1_ser = world.entity('Player'.into(), p1_key.into(), 0, SerdeLen::<Player>::len());
let p1 = Serde::<Player>::deserialize(ref p1_ser).expect('player not found');

// player 1 deposit calldata
let mut bank_calldata = ArrayTrait::new();
bank_calldata.append(game_id.into());
bank_calldata.append((1 * SCALING_FACTOR).into());

// player 1 deposit 1
let res_deposit = world.execute('bank_deposit'.into(), bank_calldata.span());

// connect as player 2
starknet::testing::set_contract_address(2.try_into().unwrap());
let player2_id = spawn_player(world_address, game_id.into());

// player 2 deposit calldata
let mut bank_calldata = ArrayTrait::new();
bank_calldata.append(game_id.into());
bank_calldata.append((420 * SCALING_FACTOR).into());

// player 2 deposit 22
let res_deposit = world.execute('bank_deposit'.into(), bank_calldata.span());

// get player 1 bank data
let mut bank1_ser = world
.entity('Bank'.into(), (game_id, player1_id).into(), 0, SerdeLen::<Bank>::len());
let bank1 = Serde::<Bank>::deserialize(ref bank1_ser).expect('bank deser failed');

// get player 2 bank data
let mut bank2_ser = world
.entity('Bank'.into(), (game_id, player2_id).into(), 0, SerdeLen::<Bank>::len());
let bank2 = Serde::<Bank>::deserialize(ref bank2_ser).expect('bank deser failed');

assert(bank1.amount == (1 * SCALING_FACTOR).into(), 'invalid bank 1 amount');
assert(bank2.amount == (420 * SCALING_FACTOR).into(), 'invalid bank 2 amount');
}

15 changes: 11 additions & 4 deletions src/tests/create.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use dojo::interfaces::{
use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait};

use dojo::test_utils::spawn_test_world;
use dojo::SerdeLen;

use rollyourown::components::game::{game, Game};
use rollyourown::components::market::{market, Market};
Expand All @@ -24,12 +25,14 @@ use rollyourown::components::drug::{drug, Drug};
use rollyourown::components::location::{location, Location};
use rollyourown::components::risks::{risks, Risks};
use rollyourown::components::name::{name, Name};
use rollyourown::components::bank::{bank, Bank};
use rollyourown::systems::travel::travel;
use rollyourown::systems::trade::{buy, sell};
use rollyourown::systems::join::join_game;
use rollyourown::systems::create::create_game;
use rollyourown::systems::player::set_name;
use rollyourown::constants::SCALING_FACTOR;
use rollyourown::systems::bank::{bank_deposit, bank_withdraw};
use rollyourown::constants::{SCALING_FACTOR, STARTING_CASH, STARTING_HEALTH};

const START_TIME: u64 = 0;
const MAX_PLAYERS: usize = 2;
Expand All @@ -48,6 +51,7 @@ fn spawn_game() -> (ContractAddress, u32, felt252) {
components.append(market::TEST_CLASS_HASH);
components.append(drug::TEST_CLASS_HASH);
components.append(name::TEST_CLASS_HASH);
components.append(bank::TEST_CLASS_HASH);

let mut systems = array::ArrayTrait::new();
systems.append(create_game::TEST_CLASS_HASH);
Expand All @@ -56,6 +60,8 @@ fn spawn_game() -> (ContractAddress, u32, felt252) {
systems.append(buy::TEST_CLASS_HASH);
systems.append(sell::TEST_CLASS_HASH);
systems.append(set_name::TEST_CLASS_HASH);
systems.append(bank_deposit::TEST_CLASS_HASH);
systems.append(bank_withdraw::TEST_CLASS_HASH);

let world = spawn_test_world(components, systems);

Expand Down Expand Up @@ -93,14 +99,15 @@ fn spawn_player(world_address: ContractAddress, game_id: felt252) -> felt252 {
let player_id = serde::Serde::<felt252>::deserialize(ref res)
.expect('spawn deserialization failed');

let mut res = world.entity('Player'.into(), (game_id, player_id).into(), 0, 0);
let mut res = world
.entity('Player'.into(), (game_id, player_id).into(), 0, SerdeLen::<Player>::len());
assert(res.len() > 0, 'player not found');

let player = serde::Serde::<Player>::deserialize(ref res)
.expect('player deserialization failed');

assert(player.health == 100, 'health mismatch');
assert(player.cash == 100 * SCALING_FACTOR, 'cash mismatch');
assert(player.health == STARTING_HEALTH, 'health mismatch');
assert(player.cash == STARTING_CASH, 'cash mismatch');
player_id
}

Expand Down
Loading