Skip to content

Commit

Permalink
Use pragma oracle for price adjustments (closes #3)
Browse files Browse the repository at this point in the history
- Changes PriceChangeEvent structure
- Removes initiate_price_change and all sub-systems related to variable rate price system
  • Loading branch information
loothero authored May 22, 2024
1 parent fc5360d commit dedbc7c
Show file tree
Hide file tree
Showing 7 changed files with 42 additions and 336 deletions.
5 changes: 0 additions & 5 deletions Scarb.lock
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ name = "game"
version = "0.1.0"
dependencies = [
"arcade_account",
"game_snapshot",
"golden_token",
"lootitems",
"market",
Expand All @@ -35,10 +34,6 @@ dependencies = [
"survivor",
]

[[package]]
name = "game_snapshot"
version = "0.1.0"

[[package]]
name = "golden_token"
version = "0.1.0"
Expand Down
1 change: 0 additions & 1 deletion contracts/game/Scarb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ lootitems = { path = "../loot" }
survivor = { path = "../adventurer" }
market = { path = "../market" }
obstacles = { path = "../obstacles" }
game_snapshot = { path = "../game_snapshot" }
openzeppelin.workspace = true
golden_token.workspace = true
arcade_account.workspace = true
Expand Down
3 changes: 0 additions & 3 deletions contracts/game/src/game/interfaces.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use survivor::{
item_meta::{ItemSpecials, ItemSpecialsStorage}, leaderboard::Leaderboard,
item_primitive::{ItemPrimitive}
};
use game_snapshot::GamesPlayedSnapshot;

#[starknet::interface]
trait IGame<TContractState> {
Expand Down Expand Up @@ -44,7 +43,6 @@ trait IGame<TContractState> {
items: Array<ItemPurchase>,
);
fn update_cost_to_play(ref self: TContractState);
fn initiate_price_change(ref self: TContractState);
// ------ View Functions ------

// // adventurer details
Expand Down Expand Up @@ -157,7 +155,6 @@ trait IGame<TContractState> {
fn get_lords_address(self: @TContractState) -> ContractAddress;
fn get_leaderboard(self: @TContractState) -> Leaderboard;
fn get_cost_to_play(self: @TContractState) -> u128;
fn get_games_played_snapshot(self: @TContractState) -> GamesPlayedSnapshot;
fn can_play(self: @TContractState, golden_token_id: u256) -> bool;
}

Expand Down
136 changes: 36 additions & 100 deletions contracts/game/src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ mod Game {
const SECONDS_IN_WEEK: u32 = 604800;
const PHASE2_START: u8 = 12;
const PHASE3_START: u8 = 24;
const TARGET_PRICE_USD_CENTS: u16 = 300;
const PRAGMA_LORDS_KEY: felt252 = 'LORDS/USD'; // felt252 conversion of "LORDS/USD"

use core::{
array::{SpanTrait, ArrayTrait}, integer::u256_try_as_non_zero, traits::{TryInto, Into},
Expand All @@ -46,6 +48,8 @@ mod Game {
};

use pragma_lib::abi::{IRandomnessDispatcher, IRandomnessDispatcherTrait};
use pragma_lib::abi::{IPragmaABIDispatcher, IPragmaABIDispatcherTrait};
use pragma_lib::types::{AggregationMode, DataType, PragmaPricesResponse};

use arcade_account::{
account::interface::{
Expand Down Expand Up @@ -97,7 +101,6 @@ mod Game {
constants::{CombatSettings::STRENGTH_DAMAGE_BONUS, CombatEnums::{Slot, Tier, Type}}
};
use beasts::beast::{Beast, IBeast, ImplBeast};
use game_snapshot::{GamesPlayedSnapshot, GamesPlayedSnapshotImpl};

#[storage]
struct Storage {
Expand All @@ -116,11 +119,11 @@ mod Game {
_golden_token_last_use: LegacyMap::<felt252, felt252>,
_golden_token: ContractAddress,
_cost_to_play: u128,
_games_played_snapshot: GamesPlayedSnapshot,
_terminal_timestamp: u64,
_adventurer_entropy: LegacyMap::<felt252, felt252>,
_randomness_contract_address: ContractAddress,
_randomness_rotation_interval: u8,
_oracle_address: ContractAddress,
}

#[event]
Expand Down Expand Up @@ -161,7 +164,8 @@ mod Game {
golden_token_address: ContractAddress,
terminal_timestamp: u64,
randomness_contract_address: ContractAddress,
randomness_rotation_interval: u8
randomness_rotation_interval: u8,
oracle_address: ContractAddress
) {
// init storage
self._lords.write(lords);
Expand All @@ -171,6 +175,7 @@ mod Game {
self._genesis_block.write(starknet::get_block_info().unbox().block_number.into());
self._randomness_contract_address.write(randomness_contract_address);
self._randomness_rotation_interval.write(randomness_rotation_interval);
self._oracle_address.write(oracle_address);

// On mainnet, set genesis timestamp to LSV1.0 genesis to preserve same reward distribution schedule for V1.1
let chain_id = starknet::get_execution_info().unbox().tx_info.unbox().chain_id;
Expand Down Expand Up @@ -566,17 +571,26 @@ mod Game {
_save_adventurer(ref self, ref adventurer, adventurer_id);
}

/// @title Updates cost to play
///
/// @notice Adjusts the price up or down
/// @dev This is intentional callable by anyone
/// @players Adjust the cost to play if the moving price of $LORDS is too high or too low
fn update_cost_to_play(ref self: ContractState) {
_update_cost_to_play(ref self);
}
let previous_price = self._cost_to_play.read();
let oracle_address = self._oracle_address.read();
let lords_price = get_asset_price_median(
oracle_address, DataType::SpotEntry(PRAGMA_LORDS_KEY)
);

// target price is the target price in cents * 10^8 because pragma uses 8 decimals for LORDS price
let target_price = TARGET_PRICE_USD_CENTS.into() * 100000000;

// new price is the target price (in cents) divided by the current lords price (in cents)
let new_price = (target_price / (lords_price * 100)) * 1000000000000000000;

fn initiate_price_change(ref self: ContractState) {
_initiate_price_change(ref self);
self._cost_to_play.write(new_price);
self
.emit(
PriceChangeEvent {
previous_price, new_price, lords_price, changer: get_caller_address()
}
);
}
// ------------------------------------------ //
// ------------ View Functions -------------- //
Expand Down Expand Up @@ -926,9 +940,6 @@ mod Game {
_get_cost_to_play(self)
}

fn get_games_played_snapshot(self: @ContractState) -> GamesPlayedSnapshot {
self._games_played_snapshot.read()
}
fn can_play(self: @ContractState, golden_token_id: u256) -> bool {
_can_play(self, golden_token_id)
}
Expand All @@ -938,6 +949,13 @@ mod Game {
// ------------ Internal Functions ---------- //
// ------------------------------------------ //

fn get_asset_price_median(oracle_address: ContractAddress, asset: DataType) -> u128 {
let oracle_dispatcher = IPragmaABIDispatcher { contract_address: oracle_address };
let output: PragmaPricesResponse = oracle_dispatcher
.get_data(asset, AggregationMode::Median(()));
return output.price;
}

fn request_randomness(
randomness_address: ContractAddress, seed: u64, adventurer_id: felt252, fee_limit: u128
) {
Expand Down Expand Up @@ -2956,10 +2974,9 @@ mod Game {

#[derive(Drop, starknet::Event)]
struct PriceChangeEvent {
previous_cost_to_play: u128,
new_cost_to_play: u128,
global_games_per_day: u64,
snapshot_games_per_day: u64,
previous_price: u128,
new_price: u128,
lords_price: u128,
changer: ContractAddress
}

Expand Down Expand Up @@ -3348,85 +3365,4 @@ mod Game {
fn _last_usage(self: @ContractState, token_id: u256) -> u256 {
self._golden_token_last_use.read(token_id.try_into().unwrap()).into()
}

fn _assert_week_past(self: @ContractState, time: u64) {
let difference: u64 = get_block_timestamp() - time;

// check if time diff is greater than a week
let one_week: u64 = (SECONDS_IN_DAY.into() * 7).try_into().unwrap();

// assert enough time passed
assert(difference >= one_week, messages::TIME_NOT_REACHED);
}

fn _initiate_price_change(ref self: ContractState) {
// get current snapshot and verify it's not locked, pending a price change
// @dev this protects this function from being spammed and ensures that
// calls to this function are followed by a price change consideration
let current_snapshot = self._games_played_snapshot.read();
assert(current_snapshot.locked == 0, 'price change already initiated');

let timestamp = get_block_timestamp();
let game_count = self._game_counter.read().try_into().unwrap();

// initialize a new game snapshot with locked set to true
// @dev the price change will unlock it
let game_snapshot = GamesPlayedSnapshot { timestamp, game_count, locked: 1 };

// save snapshot
self._games_played_snapshot.write(game_snapshot);
}

fn _update_cost_to_play(ref self: ContractState) {
// get the current games played snapshot
let snapshot = self._games_played_snapshot.read();

// assert that the snapshot is locked
assert(snapshot.locked == 1, 'price change not initiated');

// assert the time between the snapshot and current timestamp is a week
_assert_week_past(@self, snapshot.timestamp);

// unlock the game snapshot so that initiate price change can be called again
self._games_played_snapshot.write(snapshot.unlock());

// load storage variables into local vars for cleaner var names
let current_game_count = self._game_counter.read();
let current_timestamp = get_block_timestamp();
// let current_timestamp = 86400;
let contract_deployed_timestamp = self._genesis_timestamp.read();
let previous_cost_to_play = self._cost_to_play.read();

// get average number of games played per day during the snapshott
let snapshot_games_per_day = snapshot.games_per_day(current_game_count, current_timestamp);

// get average number of games played per day from genesis to start of snapshot
let life_of_game_seconds: u128 = (snapshot.timestamp - contract_deployed_timestamp).into();

let global_games_per_day: u64 = snapshot.game_count
* SECONDS_IN_DAY.into()
/ life_of_game_seconds.try_into().unwrap();

// get the adjusted price based on the snapshot and global games per day
let new_cost_to_play = GamesPlayedSnapshotImpl::get_price_adjustment(
previous_cost_to_play, global_games_per_day, snapshot_games_per_day
);

// if the cost of the game changed
if new_cost_to_play != previous_cost_to_play {
// update the cost to play
self._cost_to_play.write(new_cost_to_play);
// emit price changed event
self
.emit(
PriceChangeEvent {
previous_cost_to_play,
new_cost_to_play,
global_games_per_day,
snapshot_games_per_day,
changer: get_caller_address()
}
);
}
}
}
5 changes: 0 additions & 5 deletions contracts/game_snapshot/Scarb.toml

This file was deleted.

Loading

0 comments on commit dedbc7c

Please sign in to comment.