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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
use anchor_lang::prelude::*;

use crate::{
    program::CyclosCore,
    states::{
        oracle::OBSERVATION_SEED, position::POSITION_SEED, tick::TICK_SEED,
        tick_bitmap::BITMAP_SEED,
    },
};

/// Seed to derive account address and signature
pub const POOL_SEED: &str = "p";

/// The pool state
///
/// PDA of `[POOL_SEED, token_0, token_1, fee]`
///
#[account(zero_copy)]
#[derive(Default)]
#[repr(packed)]
pub struct PoolState {
    /// Bump to identify PDA
    pub bump: u8,

    /// Token pair of the pool, where token_0 address < token_1 address
    pub token_0: Pubkey,
    pub token_1: Pubkey,

    /// Fee amount for swaps, denominated in hundredths of a bip (i.e. 1e-6)
    pub fee: u32,

    /// The minimum number of ticks between initialized ticks
    pub tick_spacing: u16,

    /// The currently in range liquidity available to the pool.
    /// This value has no relationship to the total liquidity across all ticks.
    pub liquidity: u64,

    /// The current price of the pool as a sqrt(token_1/token_0) Q32.32 value
    pub sqrt_price_x32: u64,

    /// The current tick of the pool, i.e. according to the last tick transition that was run.
    /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the
    /// price is on a tick boundary.
    /// Not necessarily a multiple of tick_spacing.
    pub tick: i32,

    /// the most-recently updated index of the observations array
    pub observation_index: u16,

    /// the current maximum number of observations that are being stored
    pub observation_cardinality: u16,

    /// The next maximum number of observations to store, triggered on a swap or position update
    pub observation_cardinality_next: u16,

    /// The fee growth as a Q32.32 number, i.e. fees of token_0 and token_1 collected per
    /// unit of liquidity for the entire life of the pool.
    /// These values can overflow u64
    pub fee_growth_global_0_x32: u64,
    pub fee_growth_global_1_x32: u64,

    /// The amounts of token_0 and token_1 that are owed to the protocol.
    /// Protocol fees will never exceed u64::MAX in either token
    pub protocol_fees_token_0: u64,
    pub protocol_fees_token_1: u64,

    /// Whether the pool is currently locked to reentrancy
    pub unlocked: bool,
}

impl PoolState {
    /// Returns the observation index after the currently active one in a liquidity pool
    ///
    /// # Arguments
    /// * `self` - A pool account
    ///
    pub fn next_observation_index(self) -> u16 {
        (self.observation_index + 1) % self.observation_cardinality_next
    }

    /// Validates the public key of an observation account
    ///
    /// # Arguments
    ///
    /// * `self`- The pool to which the account belongs
    /// * `key` - The address to validated
    /// * `bump` - The PDA bump for the address
    /// * `next` - Whether to validate the current observation account or the next account
    ///
    pub fn validate_observation_address(self, key: &Pubkey, bump: u8, next: bool) -> Result<()> {
        let index = if next {
            self.next_observation_index()
        } else {
            self.observation_index
        };
        let seeds = [
            &OBSERVATION_SEED.as_bytes(),
            self.token_0.as_ref(),
            self.token_1.as_ref(),
            &self.fee.to_be_bytes(),
            &index.to_be_bytes(),
            &[bump],
        ];
        assert!(*key == Pubkey::create_program_address(&seeds, &CyclosCore::id()).unwrap());
        Ok(())
    }

    /// Validates the public key of a tick account
    ///
    /// # Arguments
    ///
    /// * `self`- The pool to which the account belongs
    /// * `key` - The address to validated
    /// * `bump` - The PDA bump for the address
    /// * `tick` - The tick from which the address should be derived
    ///
    pub fn validate_tick_address(self, key: &Pubkey, bump: u8, tick: i32) -> Result<()> {
        assert!(
            *key == Pubkey::create_program_address(
                &[
                    &TICK_SEED.as_bytes(),
                    self.token_0.as_ref(),
                    self.token_1.as_ref(),
                    &self.fee.to_be_bytes(),
                    &tick.to_be_bytes(),
                    &[bump],
                ],
                &CyclosCore::id(),
            )
            .unwrap(),
        );
        Ok(())
    }

    /// Validates the public key of a bitmap account
    ///
    /// # Arguments
    ///
    /// * `self`- The pool to which the account belongs
    /// * `key` - The address to validated
    /// * `bump` - The PDA bump for the address
    /// * `tick` - The tick from which the address should be derived
    ///
    pub fn validate_bitmap_address(self, key: &Pubkey, bump: u8, word_pos: i16) -> Result<()> {
        assert!(
            *key == Pubkey::create_program_address(
                &[
                    &BITMAP_SEED.as_bytes(),
                    self.token_0.as_ref(),
                    self.token_1.as_ref(),
                    &self.fee.to_be_bytes(),
                    &word_pos.to_be_bytes(),
                    &[bump],
                ],
                &CyclosCore::id(),
            )
            .unwrap(),
        );
        Ok(())
    }

    /// Validates the public key of a bitmap account
    ///
    /// # Arguments
    ///
    /// * `self`- The pool to which the account belongs
    /// * `key` - The address to validated
    /// * `bump` - The PDA bump for the address
    /// * `tick` - The tick from which the address should be derived
    ///
    pub fn validate_position_address(
        self,
        key: &Pubkey,
        bump: u8,
        position_owner: &Pubkey,
        tick_lower: i32,
        tick_upper: i32,
    ) -> Result<()> {
        assert!(
            *key == Pubkey::create_program_address(
                &[
                    &POSITION_SEED.as_bytes(),
                    self.token_0.as_ref(),
                    self.token_1.as_ref(),
                    &self.fee.to_be_bytes(),
                    position_owner.as_ref(),
                    &tick_lower.to_be_bytes(),
                    &tick_upper.to_be_bytes(),
                    &[bump],
                ],
                &CyclosCore::id(),
            )
            .unwrap(),
        );
        Ok(())
    }
}

/// Emitted when a pool is created and initialized with a starting price
///
#[event]
pub struct PoolCreatedAndInitialized {
    /// The first token of the pool by address sort order
    #[index]
    pub token_0: Pubkey,

    /// The second token of the pool by address sort order
    #[index]
    pub token_1: Pubkey,

    /// The fee collected upon every swap in the pool, denominated in hundredths of a bip
    #[index]
    pub fee: u32,

    /// The minimum number of ticks between initialized ticks
    pub tick_spacing: u16,

    /// The address of the created pool
    pub pool_state: Pubkey,

    /// The initial sqrt price of the pool, as a Q32.32
    pub sqrt_price_x32: u64,

    /// The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool
    pub tick: i32,
}

/// Emitted when the collected protocol fees are withdrawn by the factory owner
#[event]
pub struct CollectProtocolEvent {
    /// The pool whose protocol fee is collected
    #[index]
    pub pool_state: Pubkey,

    /// The address that collects the protocol fees
    #[index]
    pub sender: Pubkey,

    /// The address that receives the collected token_0 protocol fees
    pub recipient_wallet_0: Pubkey,

    /// The address that receives the collected token_1 protocol fees
    pub recipient_wallet_1: Pubkey,

    /// The amount of token_0 protocol fees that is withdrawn
    pub amount_0: u64,

    /// The amount of token_0 protocol fees that is withdrawn
    pub amount_1: u64,
}

/// Emitted by when a swap is performed for a pool
#[event]
pub struct SwapEvent {
    /// The pool for which token_0 and token_1 were swapped
    #[index]
    pub pool_state: Pubkey,

    /// The address that initiated the swap call, and that received the callback
    #[index]
    pub sender: Pubkey,

    /// The payer token account in zero for one swaps, or the recipient token account
    /// in one for zero swaps
    #[index]
    pub token_account_0: Pubkey,

    /// The payer token account in one for zero swaps, or the recipient token account
    /// in zero for one swaps
    #[index]
    pub token_account_1: Pubkey,

    /// The delta of the token_0 balance of the pool
    pub amount_0: i64,

    /// The delta of the token_1 balance of the pool
    pub amount_1: i64,

    /// The sqrt(price) of the pool after the swap, as a Q32.32
    pub sqrt_price_x32: u64,

    /// The liquidity of the pool after the swap
    pub liquidity: u64,

    /// The log base 1.0001 of price of the pool after the swap
    pub tick: i32,
}