Skip to content
1 change: 1 addition & 0 deletions algebra-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod monoid;
pub mod semigroup;
14 changes: 9 additions & 5 deletions algebra-core/src/monoid.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
pub trait Monoid {
use crate::semigroup::Semigroup;

pub trait Monoid: Semigroup {
fn empty() -> Self;
fn combine(self, other: Self) -> Self;
}

impl Semigroup for u64 {
fn combine(self, other: Self) -> Self {
self + other
}
}

impl Monoid for u64 {
fn empty() -> Self {
0
}
fn combine(self, other: Self) -> Self {
self + other
}
}
3 changes: 3 additions & 0 deletions algebra-core/src/semigroup.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub trait Semigroup {
fn combine(self, other: Self) -> Self;
}
1 change: 1 addition & 0 deletions bloom-derivation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub fn derive_fragment(input: TokenStream) -> TokenStream {
type U;
fn side(&self) -> bloom_offchain::execution_engine::liquidity_book::side::SideM;
fn input(&self) -> bloom_offchain::execution_engine::liquidity_book::types::InputAsset<u64>;
fn output(&self) -> bloom_offchain::execution_engine::liquidity_book::types::OutputAsset<u64>;
fn price(&self) -> bloom_offchain::execution_engine::liquidity_book::types::AbsolutePrice;
fn linear_fee(&self, input_consumed: bloom_offchain::execution_engine::liquidity_book::types::InputAsset<u64>) -> bloom_offchain::execution_engine::liquidity_book::types::FeeAsset<u64>;
fn fee(&self) -> bloom_offchain::execution_engine::liquidity_book::types::FeeAsset<u64>;
Expand Down
8 changes: 6 additions & 2 deletions bloom-offchain-cardano/src/orders/limit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use cml_crypto::{blake2b224, Ed25519KeyHash, RawBytesEncoding};
use cml_multi_era::babbage::BabbageTransactionOutput;

use bloom_offchain::execution_engine::liquidity_book::fragment::{Fragment, OrderState, StateTrans};
use bloom_offchain::execution_engine::liquidity_book::linear_output_rel;
use bloom_offchain::execution_engine::liquidity_book::linear_output_relative;
use bloom_offchain::execution_engine::liquidity_book::side::SideM;
use bloom_offchain::execution_engine::liquidity_book::time::TimeBounds;
use bloom_offchain::execution_engine::liquidity_book::types::{
Expand Down Expand Up @@ -148,6 +148,10 @@ impl Fragment for LimitOrder {
self.input_amount
}

fn output(&self) -> OutputAsset<u64> {
self.output_amount
}

fn price(&self) -> AbsolutePrice {
AbsolutePrice::from_price(self.side(), self.base_price)
}
Expand Down Expand Up @@ -311,7 +315,7 @@ where
.checked_sub(reserved_lovelace)
.and_then(|lov| lov.checked_sub(conf.fee))
.and_then(|lov| lov.checked_sub(tradable_lovelace))?;
if let Some(base_output) = linear_output_rel(conf.tradable_input, conf.base_price) {
if let Some(base_output) = linear_output_relative(conf.tradable_input, conf.base_price) {
let min_marginal_output = conf.min_marginal_output;
let max_execution_steps_possible = base_output.checked_div(min_marginal_output);
let max_execution_steps_available = execution_budget.checked_div(conf.cost_per_ex_step);
Expand Down
198 changes: 198 additions & 0 deletions bloom-offchain/src/execution_engine/liquidity_book/core.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
use std::collections::HashMap;

use either::Either;

use algebra_core::monoid::Monoid;
use algebra_core::semigroup::Semigroup;
use spectrum_offchain::data::Stable;

use crate::execution_engine::liquidity_book::fragment::Fragment;
use crate::execution_engine::liquidity_book::side::SideM;
use crate::execution_engine::liquidity_book::types::{FeeAsset, InputAsset, OutputAsset};

/// Usage of liquidity from market maker.
/// take(P, M_1 + M_2 + ... + M_n) = take(P, M_1) |> take(_, M_2) |> ... take(_, M_n)
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct Make {
pub side: SideM,
pub input: InputAsset<u64>,
pub output: OutputAsset<u64>,
}

impl Semigroup for Make {
fn combine(self, other: Self) -> Self {
todo!("Semigroup for Make")
}
}

/// Taking liquidity from market.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct TerminalTake {
/// Input asset removed as a result of this transaction.
pub removed_input: InputAsset<u64>,
/// Output asset added as a result of this transaction.
pub added_output: OutputAsset<u64>,
/// Overall execution budget used.
pub budget_used: FeeAsset<u64>,
/// Execution fee charged.
pub fee_used: FeeAsset<u64>,
}

impl TerminalTake {
pub fn new() -> Self {
Self {
removed_input: 0,
added_output: 0,
budget_used: 0,
fee_used: 0,
}
}
}

#[derive(Debug, Copy, Clone)]
pub enum Next<S, T> {
/// Successive state is available.
Succ(S),
/// Terminal state.
Term(T),
}

/// State transition of a take.
#[derive(Debug, Copy, Clone)]
pub struct Trans<Cont, Term> {
pub target: Cont,
pub result: Next<Cont, Term>,
}

impl<Cont, Term> Semigroup for Trans<Cont, Term> {
fn combine(self, other: Self) -> Self {
Self {
target: self.target,
result: other.result,
}
}
}

pub type TakerTrans<Taker> = Trans<Taker, TerminalTake>;

impl<T> TakerTrans<T> {
pub fn added_output(&self) -> OutputAsset<u64>
where
T: Fragment,
{
let accumulated_output = match &self.result {
Next::Succ(next) => next.output(),
Next::Term(term) => term.added_output,
};
accumulated_output - self.target.output()
}
}

#[derive(Debug, Copy, Clone)]
pub struct TryApply<Action, Subject> {
pub action: Action,
pub target: Subject,
pub result: Next<Subject, ()>,
}

impl<A, S> Semigroup for TryApply<A, S>
where
A: Semigroup,
{
fn combine(self, other: Self) -> Self {
Self {
action: self.action.combine(other.action),
target: self.target,
result: other.result,
}
}
}

#[derive(Debug, Clone)]
pub struct MatchmakingAttempt<Taker: Stable, Maker: Stable, U> {
takes: HashMap<Taker::StableId, TakerTrans<Taker>>,
makes: HashMap<Maker::StableId, TryApply<Make, Maker>>,
execution_units_consumed: U,
}

impl<Taker: Stable, Maker: Stable, U> MatchmakingAttempt<Taker, Maker, U> {
pub fn empty() -> Self
where
U: Monoid,
{
Self {
takes: HashMap::new(),
makes: HashMap::new(),
execution_units_consumed: U::empty(),
}
}

pub fn execution_units_consumed(&self) -> U
where
U: Copy,
{
self.execution_units_consumed
}

pub fn is_complete(&self) -> bool {
self.takes.len() > 0
}

pub fn unsatisfied_fragments(&self) -> Vec<Taker>
where
Taker: Fragment + Copy,
{
let not_ok_terminal_takes = self.takes.iter().filter_map(|(_, apply)| {
let target = apply.target;
if apply.added_output() < target.min_marginal_output() {
Some(target)
} else {
None
}
});
not_ok_terminal_takes.collect()
}

pub fn add_take(&mut self, take: TakerTrans<Taker>) {
let sid = take.target.stable_id();
let take_combined = match self.takes.remove(&sid) {
None => take,
Some(existing_transition) => existing_transition.combine(take),
};
self.takes.insert(sid, take_combined);
}

pub fn add_make(&mut self, make: TryApply<Make, Maker>) {
let sid = make.target.stable_id();
let maker_combined = match self.makes.remove(&sid) {
None => make,
Some(existing_transition) => existing_transition.combine(make),
};
self.makes.insert(sid, maker_combined);
}
}

#[derive(Debug, Copy, Clone)]
pub struct Applied<Action, Subject: Stable> {
pub action: Action,
pub target: Subject::StableId,
pub result: Next<Subject, ()>,
}

#[derive(Debug, Clone)]
pub struct MatchmakingRecipe<Taker: Stable, Maker: Stable> {
instructions: Vec<Either<Trans<Taker, TerminalTake>, Applied<Make, Maker>>>,
}

impl<Taker, Maker> MatchmakingRecipe<Taker, Maker>
where
Taker: Stable,
Maker: Stable,
{
pub fn try_from<U>(attempt: MatchmakingAttempt<Taker, Maker, U>) -> Result<Self, Option<Vec<Taker>>>
where
Taker: Fragment + Copy,
{
Err(None)
}
}
15 changes: 14 additions & 1 deletion bloom-offchain/src/execution_engine/liquidity_book/fragment.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::execution_engine::liquidity_book::core::{TerminalTake, Trans};
use num_rational::Ratio;
use std::fmt::{Display, Formatter};

Expand All @@ -17,6 +18,16 @@ pub trait OrderState: Sized {
) -> (StateTrans<Self>, ExBudgetUsed, ExFeeUsed);
}

/// Order as a state machine.
pub trait TakerBehaviour: Sized {
fn with_updated_time(self, time: u64) -> Trans<Self, TerminalTake>;
fn with_applied_trade(
self,
removed_input: InputAsset<u64>,
added_output: OutputAsset<u64>,
) -> Trans<Self, TerminalTake>;
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum StateTrans<T> {
/// Next state is available.
Expand Down Expand Up @@ -53,8 +64,10 @@ pub trait Fragment {
type U;
/// Side of the fragment relative to pair it maps to.
fn side(&self) -> SideM;
/// Input asset.
/// Amount of input asset remaining.
fn input(&self) -> InputAsset<u64>;
/// Amount of output asset accumulated.
fn output(&self) -> OutputAsset<u64>;
/// Price of base asset in quote asset.
fn price(&self) -> AbsolutePrice;
/// Batcher fee for whole swap.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
use crate::execution_engine::liquidity_book::core::{Make, TryApply};
use crate::execution_engine::liquidity_book::side::Side;
use crate::execution_engine::liquidity_book::types::AbsolutePrice;
use derive_more::{Display, Div, From, Into, Mul};
use num_rational::Ratio;
use std::cmp::Ordering;
use bignumber::BigNumber;

/// Price of a theoretical 0-swap in pool.
#[repr(transparent)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Div, Mul, From, Into, Display)]
pub struct StaticPrice(AbsolutePrice);
pub struct SpotPrice(AbsolutePrice);

impl StaticPrice {
impl SpotPrice {
pub fn unwrap(self) -> Ratio<u128> {
self.0.unwrap()
}
}

/// Pooled liquidity.
pub trait Pool {
pub trait MarketMaker {
type U;
/// Static price (regardless swap vol) in this pool.
fn static_price(&self) -> StaticPrice;
fn static_price(&self) -> SpotPrice;
/// Real price of swap.
fn real_price(&self, input: Side<u64>) -> AbsolutePrice;
/// Output of a swap.
Expand All @@ -28,9 +30,16 @@ pub trait Pool {
fn quality(&self) -> PoolQuality;
/// How much (approximately) execution of this fragment will cost.
fn marginal_cost_hint(&self) -> Self::U;
// Determine is swaps allowed for current pool, based on lq_bound.
// Used for correct support of legacy v1/v2 and fee switch pools
fn swaps_allowed(&self) -> bool;
// Is this maker active at the moment or not.
fn is_active(&self) -> bool;
fn available_liquidity_by_user_impact(&self, max_user_price_impact: Side<Ratio<u64>>) -> (u64, u64);
fn available_liquidity_by_spot_impact(&self, max_spot_price_impact: Side<Ratio<u64>>) -> (u64, u64);
}

/// Pooled liquidity.
pub trait MakerBehavior: Sized {
/// Output of a swap.
fn swap(self, input: Side<u64>) -> TryApply<Make, Self>;
}

#[derive(Debug, Copy, Clone, Eq, PartialEq, Into, From, Display)]
Expand Down
Loading