Skip to content

Commit

Permalink
feat(refactor): add execution to arbitrageur
Browse files Browse the repository at this point in the history
  • Loading branch information
ts0yu authored Aug 27, 2024
1 parent 135d41e commit a1150d0
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 15 deletions.
108 changes: 108 additions & 0 deletions Cargo.lock

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

11 changes: 10 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,20 @@ edition = "2021"
repository = "https://github.com/arena-rs/arena"
description = "Framework for holistic economic modelling and simulation of Uniswap v4 strategies, hooks and pools."
license-file = "./LICENSE"
exclude = ["contracts"]
exclude = ["contracts", "cli"]

[lib]
name = "libarena"
path = "src/lib.rs"

[[bin]]
name = "arena"
path = "src/bin/cli.rs"

[dependencies]
rug = "1.25.0"
rand = "0.8.5"
clap = { version = "4.5.16", features = ["derive"] }
plotly = "0.9.0"
rand_distr = "0.4.3"
async-trait = "0.1.81"
Expand Down
2 changes: 1 addition & 1 deletion contracts/utils/src/Fetcher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,4 @@ contract Fetcher {
// slot key of the tick key: `pools[poolId].ticks[tick]
return keccak256(abi.encodePacked(int256(tick), ticksMappingSlot));
}
}
}
5 changes: 1 addition & 4 deletions src/arena.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ impl<V> Arena<V> {
(*currency_0.address(), *currency_1.address());
}

let call = pool_manager
pool_manager
.initialize(
self.pool.clone(),
U256::from(79228162514264337593543950336_u128),
Expand Down Expand Up @@ -188,8 +188,6 @@ pub struct ArenaBuilder<V> {

/// [`Arena::arbitrageur`]
pub arbitrageur: Option<Box<dyn Arbitrageur>>,

providers: Option<HashMap<usize, AnvilProvider>>,
}

impl<V> Default for ArenaBuilder<V> {
Expand All @@ -206,7 +204,6 @@ impl<V> ArenaBuilder<V> {
strategies: Vec::new(),
pool: PoolKey::default(),
feed: None,
providers: None,
inspector: None,
arbitrageur: None,
}
Expand Down
56 changes: 47 additions & 9 deletions src/engine/arbitrageur.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,45 @@
use std::{cmp::Ordering, str::FromStr};
use std::str::FromStr;

use alloy::primitives::Signed;
use alloy::primitives::{Address, Bytes, Signed};
use async_trait::async_trait;
use rug::{ops::Pow, Float};

use crate::{
types::{Fetcher, PoolManager::SwapParams},
types::{
Fetcher, PoolSwapTest,
PoolSwapTest::{SwapParams, TestSettings},
},
AnvilProvider, Signal,
};

/// Generic trait allowing user defined arbitrage strategies.
#[async_trait]
pub trait Arbitrageur {
/// Initialixe arbitrageur agent.
async fn init(&mut self, signal: &Signal, provider: AnvilProvider);

/// Perform an arbitrage based on a [`Signal`].
async fn arbitrage(&self, signal: &Signal, provider: AnvilProvider);
async fn arbitrage(&mut self, signal: &Signal, provider: AnvilProvider);
}

/// Default implementation of an [`Arbitrageur`] that uses the closed-form optimal swap amount to determine the optimal arbitrage.
pub struct DefaultArbitrageur;
pub struct DefaultArbitrageur {
swapper: Address,
}

#[async_trait]
impl Arbitrageur for DefaultArbitrageur {
async fn arbitrage(&self, signal: &Signal, provider: AnvilProvider) {
async fn init(&mut self, signal: &Signal, provider: AnvilProvider) {
let swapper = PoolSwapTest::deploy(provider.clone(), signal.manager)
.await
.unwrap();

self.swapper = *swapper.address();
}

async fn arbitrage(&mut self, signal: &Signal, provider: AnvilProvider) {
let swapper = PoolSwapTest::new(self.swapper, provider.clone());

let base = Float::with_val(53, 1.0001);
let price = Float::with_val(53, signal.current_value);

Expand Down Expand Up @@ -49,13 +67,32 @@ impl Arbitrageur for DefaultArbitrageur {
let optimal_swap =
Float::with_val(53, 0).max(&(a.clone() - (k / (signal.pool.fee * (a / b)))));

let zero_for_one = &current_tick > &target_tick;
let zero_for_one = current_tick > target_tick;

let swap_params = SwapParams {
amountSpecified: Signed::from_str(&optimal_swap.to_string()).unwrap(),
zeroForOne: zero_for_one,
sqrtPriceLimitX96: signal.sqrt_price_x96,
};

let test_settings = TestSettings {
takeClaims: false,
settleUsingBurn: false,
};

swapper
.swap(
signal.pool.clone().into(),
swap_params,
test_settings,
Bytes::new(),
)
.send()
.await
.unwrap()
.watch()
.await
.unwrap();
}
}

Expand All @@ -67,7 +104,7 @@ impl DefaultArbitrageur {
start: i32,
end: i32,
) -> (Float, Float) {
let fetcher = Fetcher::new(signal.fetcher, provider);
let fetcher = Fetcher::new(signal.fetcher, provider.clone());

let mut liquidity_a = Float::with_val(53, 0);
let mut liquidity_b = Float::with_val(53, 0);
Expand Down Expand Up @@ -102,5 +139,6 @@ pub struct EmptyArbitrageur;

#[async_trait]
impl Arbitrageur for EmptyArbitrageur {
async fn arbitrage(&self, _signal: &Signal, _provider: AnvilProvider) {}
async fn init(&mut self, _signal: &Signal, _provider: AnvilProvider) {}
async fn arbitrage(&mut self, _signal: &Signal, _provider: AnvilProvider) {}
}
33 changes: 33 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![warn(missing_docs)]
//! Arena
/// Defines the main simulation runtime.
pub mod arena;
Expand Down Expand Up @@ -45,6 +46,7 @@ mod types {

use crate::types::{
Fetcher::PoolKey as FetcherPoolKey, PoolManager::PoolKey as ManagerPoolKey,
PoolSwapTest::PoolKey as SwapPoolKey,
};

sol! {
Expand Down Expand Up @@ -75,6 +77,13 @@ mod types {
"src/artifacts/Fetcher.json"
}

sol! {
#[sol(rpc)]
#[derive(Debug)]
PoolSwapTest,
"src/artifacts/PoolSwapTest.json"
}

impl From<FetcherPoolKey> for ManagerPoolKey {
fn from(fetcher: FetcherPoolKey) -> Self {
ManagerPoolKey {
Expand All @@ -98,6 +107,30 @@ mod types {
}
}
}

impl From<SwapPoolKey> for ManagerPoolKey {
fn from(swap: SwapPoolKey) -> Self {
ManagerPoolKey {
currency0: swap.currency0,
currency1: swap.currency1,
fee: swap.fee,
tickSpacing: swap.tickSpacing,
hooks: swap.hooks,
}
}
}

impl From<ManagerPoolKey> for SwapPoolKey {
fn from(manager: ManagerPoolKey) -> Self {
SwapPoolKey {
currency0: manager.currency0,
currency1: manager.currency1,
fee: manager.fee,
tickSpacing: manager.tickSpacing,
hooks: manager.hooks,
}
}
}
}

/// A signal that is passed to a [`Strategy`] to provide information about the current state of the pool.
Expand Down

0 comments on commit a1150d0

Please sign in to comment.