Skip to content

Commit

Permalink
chore: trait specialization
Browse files Browse the repository at this point in the history
  • Loading branch information
0xJepsen committed Apr 25, 2024
1 parent ee4c850 commit 02dee0a
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 48 deletions.
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions kit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ keywords = ["ethereum", "smart-contracts", "automated market makers"]
readme = "../README.md"

[dependencies]
arbiter-core = { git = "https://github.com/primitivefinance/arbiter.git", rev = "bd08b647" }
arbiter-engine = { git = "https://github.com/primitivefinance/arbiter.git", rev = "bd08b647" }
arbiter-macros = { git = "https://github.com/primitivefinance/arbiter.git", rev = "bd08b647" }
arbiter-bindings = { git = "https://github.com/primitivefinance/arbiter.git", rev = "bd08b647" }
arbiter-core = { git = "https://github.com/primitivefinance/arbiter.git", rev = "50d913a6" }
arbiter-engine = { git = "https://github.com/primitivefinance/arbiter.git", rev = "50d913a6" }
arbiter-macros = { git = "https://github.com/primitivefinance/arbiter.git", rev = "50d913a6" }
arbiter-bindings = { git = "https://github.com/primitivefinance/arbiter.git", rev = "50d913a6" }

# Ethereum
ethers = "2.0.13"
Expand Down
51 changes: 33 additions & 18 deletions kit/src/behaviors/swap/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub trait SwapType<E> {
fn compute_swap_amount(&self, event: E) -> (eU256, InputToken);
}

// this is somewhat all encompassing. It has everything.
#[derive(Clone, Debug, Serialize, Deserialize, State)]
pub struct Swap<S, T: SwapType<E>, E>
where
Expand All @@ -16,30 +17,19 @@ where
pub _phantom: PhantomData<E>,
}

pub trait SwapStream<SwapType, E> where E: Send + 'static {
fn get_typed_stream(swap_type: SwapType, channel: Messager, client: Arc<ArbiterMiddleware>) -> Result<Option<EventStream<E>>>;
}


#[derive(Deserialize, Clone)]
pub struct SwapOnce {
pub amount: eU256,
pub input: InputToken,
}


impl<P, SwapOnce, Message> SwapStream<SwapOnce, Message> for Swap<Processing<P>, SwapOnce, Message>
where
P: PoolType + Send + Sync,
{
fn get_typed_stream(swap_type: SwapOnce, channel: Messager, client: Arc<ArbiterMiddleware>) -> Result<Option<EventStream<Message>>> {
let thing = channel.stream()?;
Ok(Some(thing))
impl SwapType<Message> for SwapOnce {
fn compute_swap_amount(&self, _event: Message) -> (eU256, InputToken) {
(self.amount, self.input)
}
}

// TODO: This needs to be configurable in some way to make the `SwapType` become
// transparent and useful.
// Should also get some data necessary for mint amounts and what not.
#[derive(Clone, Debug, Serialize, Deserialize, State)]
pub struct Config<P>
Expand Down Expand Up @@ -143,18 +133,43 @@ where
}
}

/// This is the default implementation for any processor that takes in some event E and will work for the `Swap` struct.
#[async_trait::async_trait]
impl<P, T, E> Processor<E> for Swap<Processing<P>, T, E>
where
P: PoolType + Send + Sync,
T: SwapType<E> + Send + Clone,
E: Send + 'static,
Swap<Processing<P>, T, E>: SwapStream<T, E>,
{
async fn get_stream(&mut self) -> Result<Option<EventStream<E>>> {
behaviors::swap::Swap::<behaviors::swap::Processing<P>, T, E>::get_typed_stream(self.swap_type.clone(), self.data.messager.clone(), self.data.client)
default async fn get_stream(&mut self) -> Result<Option<EventStream<E>>> {
Ok(None)
}

default async fn process(&mut self, event: E) -> Result<ControlFlow> {
let (swap_amount, input) = self.swap_type.compute_swap_amount(event);
self.data.pool.swap(swap_amount, input).await?;
Ok(ControlFlow::Continue)
}
}

/// With the `specialization` feature in Rust, we can take the above trait and use it as a default when we don't want to have some specific kind of stream come with it.
/// Sadly, we still have to copy the `process` method along with the `get_stream`, ideally, in the future, all you have to do is just implement your own `get_stream` given whatever event `E` you want to produce, and this will be a very straightforward specialization
/// that allows you to easily extend `Swap` for whatever swap type you want. See this tracking RFC for trait `specialization` https://github.com/rust-lang/rust/issues/31844
///
/// Just to be clear, this now will allow you to work with any `Swap` and `SwapType` as long as it streams `Message`s. If you need to stream something else, just copy this specialization and use whatever stream item you'd like!
#[async_trait::async_trait]
impl<P, T> Processor<Message> for Swap<Processing<P>, T, Message>
where
P: PoolType + Send + Sync,
T: SwapType<Message> + Send + Clone,
{
// This is the specialized trait for the specific type E = Message. Cool!
async fn get_stream(&mut self) -> Result<Option<EventStream<Message>>> {
Ok(Some(self.data.messager.stream()?))
}
async fn process(&mut self, event: E) -> Result<ControlFlow> {

// Would be nice to nice to not have to rewrite this every time see above... RIP
async fn process(&mut self, event: Message) -> Result<ControlFlow> {
let (swap_amount, input) = self.swap_type.compute_swap_amount(event);
self.data.pool.swap(swap_amount, input).await?;
Ok(ControlFlow::Continue)
Expand Down
4 changes: 4 additions & 0 deletions kit/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#![allow(unstable_features)]
#![feature(specialization)]

pub mod behaviors;
pub mod bindings;
pub mod pool;
Expand All @@ -11,3 +14,4 @@ use ethers::types::{Address as eAddress, I256 as eI256, U256 as eU256};
pub use pool::{BaseConfig, Pool, PoolType};
use serde::{Deserialize, Serialize};
use tracing::{debug, info};

2 changes: 1 addition & 1 deletion kit/src/pool/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ pub enum UpdateParameters<P: PoolType> {
// Notes:
// This is used in the `swap_data` function of the poolType trait to determine
// which token to swap in.
#[derive(Deserialize, Serialize, Debug, Clone)]
#[derive(Deserialize, Serialize, Debug, Clone, Copy)]
pub enum InputToken {
TokenX,
TokenY,
Expand Down
21 changes: 1 addition & 20 deletions kit/tests/common.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use std::{collections::VecDeque, marker::PhantomData};

use arbiter_core::middleware::ArbiterMiddleware;
use arbiter_engine::{agent::Agent, machine::State, messager::{Message, Messager}, world::World};
use dfmm_kit::{
behaviors::{
creator::{self, Create},
deploy::Deploy,
swap::{self, Swap, SwapStream, SwapType},
swap::{self, Swap, SwapType, SwapOnce},
token::{self, TokenAdmin},
update::{self, Update},
},
Expand Down Expand Up @@ -98,24 +97,6 @@ fn mock_swap_behavior() -> Swap::<swap::Config::<ConstantSumPool>, SwapOnce, Mes
}
}

#[derive(Deserialize, Clone)]
pub struct SwapOnce {
pub amount: eU256,
pub input: InputToken,
}

impl SwapType<Message> for SwapOnce {
fn compute_swap_amount(&self, _event: Message) -> (eU256, InputToken) {
(self.amount, self.input.clone())
}
}
impl<S> SwapStream<SwapOnce, Message> for Swap<S, SwapOnce, Message> where S: State{
fn get_typed_stream(_swap_type: SwapOnce, mut channel: Messager, _client: Arc<ArbiterMiddleware>) -> Result<Option<arbiter_engine::machine::EventStream<Message>>> {
let thing = channel.stream()?;
Ok(Some(thing))
}
}

fn mock_token_admin_behavior() -> TokenAdmin<token::Config> {
TokenAdmin::<token::Config> {
data: mock_token_data(),
Expand Down
2 changes: 1 addition & 1 deletion kit/tests/swap_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use tracing::{info, warn};
include!("common.rs");

#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
async fn run_updater_constant_sum() {
async fn run_swapper_constant_sum() {
log(Level::DEBUG);

let mut world = World::new("test");
Expand Down

0 comments on commit 02dee0a

Please sign in to comment.