1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use std::sync::Arc;

use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use starknet::{
    core::types::{Felt, StarknetError},
    providers::Provider,
};

use super::jediswap::pool::JediswapPool;
use crate::amm::tenkswap::pool::TenkSwapPool;

#[async_trait]
pub trait AutomatedMarketMaker {
    /// Returns the address of the AMM.
    fn address(&self) -> Felt;

    /// Returns a vector of tokens in the AMM.
    fn tokens(&self) -> Vec<Felt>;

    async fn sync<P>(&mut self, provider: Arc<P>) -> Result<(), StarknetError>
    where
        P: Provider + Send + Sync;

    /// Calculates a f64 representation of base token price in the AMM.
    fn calculate_price(&self, base_token: Felt, quote_token: Felt) -> Result<f64, StarknetError>;

    /// Locally simulates a swap in the AMM.
    ///
    /// Returns the amount received for `amount_in` of `token_in`.
    async fn simulate_swap<P>(
        &self,
        base_token: Felt,
        amount_in: Felt,
        provider: Arc<P>,
    ) -> Result<Felt, StarknetError>
    where
        P: Provider + Send + Sync;

    /// Locally simulates a swap in the AMM.
    /// Mutates the AMM state to the state of the AMM after swapping.
    /// Returns the amount received for `amount_in` of `token_in`.
    fn simulate_swap_mut(
        &mut self,
        base_token: Felt,
        quote_token: Felt,
        amount_in: Felt,
    ) -> Result<Felt, StarknetError>;

    // async fn populate_data<P>(&mut self, middleware: Arc<P>) -> Result<(), StarknetError>
    // where
    //     P: Provider + Sync + Send;
}

macro_rules! amm {
    ($($pool_type:ident),+ $(,)?) => {
        #[derive(Debug, Clone, Serialize, Deserialize)]
        pub enum AMM {
            $($pool_type($pool_type),)+
        }

        #[async_trait]
        impl AutomatedMarketMaker for AMM {
            fn address(&self) -> Felt{
                match self {
                    $(AMM::$pool_type(pool) => pool.address(),)+
                }
            }

            fn tokens(&self) -> Vec<Felt> {
                match self {
                    $(AMM::$pool_type(pool) => pool.tokens(),)+
                }
            }

            async fn sync<P>(&mut self, middleware: Arc<P>) -> Result<(), StarknetError>
            where
                P: Provider + Send + Sync,
            {
                match self {
                    $(AMM::$pool_type(pool) => pool.sync(middleware).await,)+
                }
            }


            async fn simulate_swap<P>(&self, base_token: Felt, amount_in: Felt, provider: Arc<P>) -> Result<Felt, StarknetError> where P: Provider + Send + Sync {
                match self {
                    $(AMM::$pool_type(pool) => pool.simulate_swap(base_token, amount_in, provider).await,)+
                }
            }

            fn simulate_swap_mut(&mut self, base_token: Felt, quote_token: Felt, amount_in: Felt) -> Result<Felt, StarknetError> {
                match self {
                    $(AMM::$pool_type(pool) => pool.simulate_swap_mut(base_token, quote_token, amount_in),)+
                }
            }


            fn calculate_price(&self, base_token: Felt, quote_token: Felt) -> Result<f64, StarknetError> {
                match self {
                    $(AMM::$pool_type(pool) => pool.calculate_price(base_token, quote_token),)+
                }
            }


            // async fn populate_data<P>(&mut self, middleware: Arc<P>) -> Result<(), StarknetError>
            // where
            //     P: Provider + Send + Sync,
            // {
            //     match self {
            //         $(AMM::$pool_type(pool) => pool.populate_data(middleware).await,)+
            //     }
            // }
        }


        impl PartialEq for AMM {
            fn eq(&self, other: &Self) -> bool {
                self.address() == other.address()
            }
        }

        impl Eq for AMM {}
    };
}

amm!(JediswapPool, TenkSwapPool);