Skip to content

Commit

Permalink
Briq factory (#14)
Browse files Browse the repository at this point in the history
Port the briq factory from Cairo 0 to dojo.

Use the component-as-logic-and-system-as-entrypoint framework.
Port the tests.
Reuse SYSTEM_CONFIG_ID for the briq factory component - might be put in the world config down the line.
  • Loading branch information
notV4l authored Aug 24, 2023
1 parent 135c370 commit 4af49e9
Show file tree
Hide file tree
Showing 11 changed files with 617 additions and 4 deletions.
2 changes: 1 addition & 1 deletion src/attributes/attributes.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use zeroable::Zeroable;
use clone::Clone;

use briq_protocol::types::{FTSpec, ShapeItem};
use briq_protocol::felt_math::{feltBitAnd, feltOrd};
use briq_protocol::felt_math::{FeltBitAnd, FeltOrd};
use briq_protocol::cumulative_balance::{CUM_BALANCE_TOKEN, CB_BRIQ, CB_ATTRIBUTES};

use dojo::world::{Context, IWorldDispatcherTrait};
Expand Down
2 changes: 1 addition & 1 deletion src/attributes/collection.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use option::OptionTrait;
use clone::Clone;
use serde::Serde;

use briq_protocol::felt_math::{feltBitAnd, feltOrd};
use briq_protocol::felt_math::{FeltBitAnd, FeltOrd};

#[derive(Component, Copy, Drop, Serde, SerdeLen)]
struct Collection {
Expand Down
3 changes: 3 additions & 0 deletions src/briq_factory.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod components;
mod systems;
mod constants;
187 changes: 187 additions & 0 deletions src/briq_factory/components.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
use starknet::ContractAddress;
use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait};

use zeroable::Zeroable;
use array::{ArrayTrait, SpanTrait};
use option::OptionTrait;
use traits::{Into, TryInto};

use briq_protocol::world_config::SYSTEM_CONFIG_ID;

use briq_protocol::briq_factory::constants::{
DECIMALS, INFLECTION_POINT, SLOPE, RAW_FLOOR,
LOWER_FLOOR, LOWER_SLOPE, DECAY_PER_SECOND, SURGE_SLOPE, MINIMAL_SURGE, SURGE_DECAY_PER_SECOND,
MIN_PURCHASE, BRIQ_MATERIAL
};

use briq_protocol::felt_math::{FeltOrd, FeltDiv};

use debug::PrintTrait;

#[derive(Component, Copy, Drop, Serde, SerdeLen)]
struct BriqFactoryStore {
#[key]
store_id: u64,
buy_token: ContractAddress,
last_stored_t: felt252,
surge_t: felt252,
last_purchase_time: u64,
}

trait BriqFactoryTrait {
fn get_briq_factory(world: IWorldDispatcher) -> BriqFactoryStore;
fn set_briq_factory(world: IWorldDispatcher, new_store: BriqFactoryStore);

fn get_current_t(self: @BriqFactoryStore) -> felt252;
fn get_surge_t(self: @BriqFactoryStore) -> felt252;
fn get_surge_price(self: @BriqFactoryStore, amount: felt252) -> felt252;
fn get_price(self: @BriqFactoryStore, amount: felt252) -> felt252;
fn get_lin_integral(
self: @BriqFactoryStore, slope: felt252, floor: felt252, t2: felt252, t1: felt252
) -> felt252;
fn get_lin_integral_negative_floor(
self: @BriqFactoryStore, slope: felt252, floor: felt252, t2: felt252, t1: felt252
) -> felt252;
fn integrate(self: @BriqFactoryStore, t: felt252, amount: felt252) -> felt252;
}

// #[generate_trait]
impl BriqFactoryStoreImpl of BriqFactoryTrait {
#[always(inline)]
fn get_briq_factory(world: IWorldDispatcher) -> BriqFactoryStore {
get!(world, (SYSTEM_CONFIG_ID), BriqFactoryStore)
}

#[always(inline)]
fn set_briq_factory(world: IWorldDispatcher, new_store: BriqFactoryStore) {
set!(world, (new_store));
}

fn get_current_t(self: @BriqFactoryStore) -> felt252 {
let store = *self; // TODO: clean this up

let time_since_last_purchase: felt252 = (starknet::info::get_block_timestamp()
- store.last_purchase_time)
.into();
let decay = time_since_last_purchase * DECAY_PER_SECOND();

if store.last_stored_t <= decay {
0
} else {
store.last_stored_t - decay
}
}

fn get_surge_t(self: @BriqFactoryStore) -> felt252 {
let store = *self; // TODO: clean this up

let time_since_last_purchase: felt252 = (starknet::info::get_block_timestamp()
- store.last_purchase_time)
.into();

let decay = time_since_last_purchase * SURGE_DECAY_PER_SECOND();

if store.surge_t <= decay {
0
} else {
store.surge_t - decay
}
}

fn get_surge_price(self: @BriqFactoryStore, amount: felt252) -> felt252 {
let surge_t = self.get_surge_t();

if (surge_t + amount) <= MINIMAL_SURGE() {
return 0;
};

if surge_t > MINIMAL_SURGE() {
self.get_lin_integral(
SURGE_SLOPE(),
0,
surge_t - MINIMAL_SURGE(),
surge_t + amount - MINIMAL_SURGE()
)
} else {
self.get_lin_integral(
SURGE_SLOPE(), 0, 0, surge_t + amount - MINIMAL_SURGE()
)
}
}


fn get_price(self: @BriqFactoryStore, amount: felt252) -> felt252 {
let t = self.get_current_t();
let price = self.integrate(t, amount * DECIMALS());
let surge = self.get_surge_price(amount * DECIMALS());

price + surge
}

fn get_lin_integral(
self: @BriqFactoryStore, slope: felt252, floor: felt252, t2: felt252, t1: felt252,
) -> felt252 {
assert(t2 < t1, 't1 >= t2');
// briq machine broke above 10^12 bricks of demand.
assert(t2 < DECIMALS() * 1000000000000, 't2 >= 10**12');
assert(t1 - t2 < DECIMALS() * 10000000000, 't1-t2 >= 10**10');

// Integral between t2 and t1:
// slope * t1 * t1 / 2 + floor * t1 - (slope * t2 * t2 / 2 + floor * t2);
// Factored as slope * (t1 + t2) * (t1 - t2) / 2 + floor * (t1 - t2);
// Then adding divisors for decimals, trying to avoid overflows and precision loss.

let interm = slope * (t1 + t2);
let q = interm / DECIMALS();
let interm = q * (t1 - t2);
let q = interm / DECIMALS() / 2;

let floor_q = floor * (t1 - t2) / DECIMALS();
q + floor_q
}


fn get_lin_integral_negative_floor(
self: @BriqFactoryStore, slope: felt252, floor: felt252, t2: felt252, t1: felt252,
) -> felt252 {
assert(t2 < t1, 't1 >= t2');
// briq machine broke above 10^12 bricks of demand.
assert(t2 < DECIMALS() * 1000000000000, 't2 >= 10**12');
assert(t1 - t2 < DECIMALS() * 10000000000, 't1-t2 >= 10**10');

// Integral between t2 and t1:
// slope * t1 * t1 / 2 + floor * t1 - (slope * t2 * t2 / 2 + floor * t2);
// Factored as slope * (t1 + t2) * (t1 - t2) / 2 + floor * (t1 - t2);
// Then adding divisors for decimals, trying to avoid overflows and precision loss.

let interm = slope * (t1 + t2);
let q = interm / DECIMALS();
let interm = q * (t1 - t2);
let q = interm / DECIMALS() / 2;
// Floor is negative. t2 < t1 so invert these, then subtract instead of adding.
let floor_q = floor * (t2 - t1) / DECIMALS();
q - floor_q
}


fn integrate(self: @BriqFactoryStore, t: felt252, amount: felt252) -> felt252 {
if (t + amount) <= INFLECTION_POINT() {
return self.get_lin_integral(
LOWER_SLOPE(), LOWER_FLOOR(), t, t + amount
);
}

if INFLECTION_POINT() <= t {
return self.get_lin_integral_negative_floor(
SLOPE(), RAW_FLOOR(), t, t + amount
);
}

self.get_lin_integral(
LOWER_SLOPE(), LOWER_FLOOR(), t, INFLECTION_POINT()
)
+ self.get_lin_integral_negative_floor(
SLOPE(), RAW_FLOOR(), INFLECTION_POINT(), t + amount
)
}
}
53 changes: 53 additions & 0 deletions src/briq_factory/constants.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// 10**18
fn DECIMALS() -> felt252 {
1000000000000000000
}

// Arbitrary inflection point below which to use the lower_* curve
fn INFLECTION_POINT() -> felt252 {
400000 * DECIMALS()
}
// Slope: Buying 100 000 briqs increases price per briq by 0.00001
fn SLOPE() -> felt252 {
100000000 // 10**8
}

// Computed to hit the inflection point at 0.00003 per briq
fn RAW_FLOOR() -> felt252 {
-1 * 10000000000000 // - 10**13
}

// Actual floor price of the briqs = 0.0001
fn LOWER_FLOOR() -> felt252 {
10000000000000 // 10**13
}

// Computed to hit the inflection point at 0.00003 per briq
fn LOWER_SLOPE() -> felt252 {
consteval_int!(5 * 10000000) // 5 * 10**7
}

// decay: for each second, reduce the price by so many wei. Computed to hit 200K per year.
fn DECAY_PER_SECOND() -> felt252 {
6337791082068820
}

fn SURGE_SLOPE() -> felt252 {
100000000 // 10**8
}

fn MINIMAL_SURGE() -> felt252 {
250000 * DECIMALS()
}

fn SURGE_DECAY_PER_SECOND() -> felt252 {
4134 * 100000000000000 // 4134 * 10**14 : Decays over a week
}

fn MIN_PURCHASE() -> felt252 {
9
}

fn BRIQ_MATERIAL() -> felt252 {
1
}
Loading

0 comments on commit 4af49e9

Please sign in to comment.