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
use crate::libraries::full_math::MulDiv;
use crate::{
    error::ErrorCode,
    libraries::{fixed_point_32, liquidity_math},
};
///! Positions represent an owner address' liquidity between a lower and upper tick boundary
///! Positions store additional state for tracking fees owed to the position
///!
use anchor_lang::prelude::*;

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

/// Info stored for each user's position
///
/// PDA of `[POSITION_SEED, token_0, token_1, fee, owner, tick_lower, tick_upper]`
///
#[account(zero_copy)]
#[derive(Default)]
#[repr(packed)]
pub struct PositionState {
    /// Bump to identify PDA
    pub bump: u8,

    /// The amount of liquidity owned by this position
    pub liquidity: u64,

    /// The token_0 fee growth per unit of liquidity as of the last update to liquidity or fees owed
    pub fee_growth_inside_0_last_x32: u64,

    /// The token_1 fee growth per unit of liquidity as of the last update to liquidity or fees owed
    pub fee_growth_inside_1_last_x32: u64,

    /// The fees owed to the position owner in token_0
    pub tokens_owed_0: u64,

    /// The fees owed to the position owner in token_1
    pub tokens_owed_1: u64,
}

impl PositionState {
    /// Credits accumulated fees to a user's position
    ///
    /// # Arguments
    ///
    /// * `self` - The individual position to update
    /// * `liquidity_delta` - The change in pool liquidity as a result of the position update
    /// * `fee_growth_inside_0_x32` - The all-time fee growth in token_0, per unit of liquidity,
    /// inside the position's tick boundaries
    /// * `fee_growth_inside_1_x32` - The all-time fee growth in token_1, per unit of liquidity,
    /// inside the position's tick boundaries
    ///
    pub fn update(
        &mut self,
        liquidity_delta: i64,
        fee_growth_inside_0_x32: u64,
        fee_growth_inside_1_x32: u64,
    ) -> Result<()> {
        let liquidity_next = if liquidity_delta == 0 {
            require!(self.liquidity > 0, ErrorCode::NP); // disallow pokes for 0 liquidity positions
            self.liquidity
        } else {
            liquidity_math::add_delta(self.liquidity, liquidity_delta)?
        };

        // calculate accumulated Fees
        let tokens_owed_0 = (fee_growth_inside_0_x32 - self.fee_growth_inside_0_last_x32)
            .mul_div_floor(self.liquidity as u64, fixed_point_32::Q32)
            .unwrap();
        let tokens_owed_1 = (fee_growth_inside_1_x32 - self.fee_growth_inside_1_last_x32)
            .mul_div_floor(self.liquidity as u64, fixed_point_32::Q32)
            .unwrap();

        // Update the position
        if liquidity_delta != 0 {
            self.liquidity = liquidity_next;
        }
        self.fee_growth_inside_0_last_x32 = fee_growth_inside_0_x32;
        self.fee_growth_inside_1_last_x32 = fee_growth_inside_1_x32;
        if tokens_owed_0 > 0 || tokens_owed_1 > 0 {
            // overflow is acceptable, have to withdraw before you hit u64::MAX fees
            self.tokens_owed_0 += tokens_owed_0;
            self.tokens_owed_1 += tokens_owed_1;
        }

        Ok(())
    }
}

/// Emitted when liquidity is minted for a given position
#[event]
pub struct MintEvent {
    /// The pool for which liquidity was minted
    #[index]
    pub pool_state: Pubkey,

    /// The address that minted the liquidity
    pub sender: Pubkey,

    /// The owner of the position and recipient of any minted liquidity
    pub owner: Pubkey,

    /// The lower tick of the position
    #[index]
    pub tick_lower: i32,

    /// The upper tick of the position
    #[index]
    pub tick_upper: i32,

    /// The amount of liquidity minted to the position range
    pub amount: u64,

    /// How much token_0 was required for the minted liquidity
    pub amount_0: u64,

    /// How much token_1 was required for the minted liquidity
    pub amount_1: u64,
}

/// Emitted when a position's liquidity is removed.
/// Does not withdraw any fees earned by the liquidity position, which must be withdrawn via #collect
#[event]
pub struct BurnEvent {
    /// The pool from where liquidity was removed
    #[index]
    pub pool_state: Pubkey,

    /// The owner of the position for which liquidity is removed
    pub owner: Pubkey,

    /// The lower tick of the position
    #[index]
    pub tick_lower: i32,

    /// The upper tick of the position
    #[index]
    pub tick_upper: i32,

    /// The amount of liquidity to remove
    pub amount: u64,

    /// The amount of token_0 withdrawn
    pub amount_0: u64,

    /// The amount of token_1 withdrawn
    pub amount_1: u64,
}

/// Emitted when fees are collected by the owner of a position
/// Collect events may be emitted with zero amount_0 and amount_1 when the caller chooses not to collect fees
#[event]
pub struct CollectEvent {
    /// The pool from which fees are collected
    #[index]
    pub pool_state: Pubkey,

    /// The owner of the position for which fees are collected
    pub owner: Pubkey,

    /// The lower tick of the position
    #[index]
    pub tick_lower: i32,

    /// The upper tick of the position
    #[index]
    pub tick_upper: i32,

    /// The amount of token_0 fees collected
    pub amount_0: u64,

    /// The amount of token_1 fees collected
    pub amount_1: u64,
}