serum_pool_schema/
schema.rs

1use std::collections::HashMap;
2use std::{io, io::Write};
3
4use borsh::schema::{Declaration, Definition};
5use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
6use solana_program::pubkey::Pubkey;
7
8/// Wrapper around `solana_sdk::pubkey::Pubkey` so it can implement `BorshSerialize` etc.
9#[repr(transparent)]
10#[derive(Clone, PartialEq, Eq)]
11pub struct Address(Pubkey);
12
13impl From<Address> for Pubkey {
14    fn from(address: Address) -> Self {
15        address.0
16    }
17}
18
19impl AsRef<Pubkey> for Address {
20    fn as_ref(&self) -> &Pubkey {
21        &self.0
22    }
23}
24
25impl AsMut<Pubkey> for Address {
26    fn as_mut(&mut self) -> &mut Pubkey {
27        &mut self.0
28    }
29}
30
31impl From<Pubkey> for Address {
32    fn from(pubkey: Pubkey) -> Self {
33        Self(pubkey)
34    }
35}
36
37impl From<&Pubkey> for Address {
38    fn from(pubkey: &Pubkey) -> Self {
39        Self(*pubkey)
40    }
41}
42
43#[macro_export]
44macro_rules! declare_tag {
45    ($name:ident, $type:ty, $tag:expr) => {
46        #[derive(Clone, PartialEq, Eq, BorshSerialize, BorshSchema, Debug)]
47        pub struct $name($type);
48        impl $name {
49            pub const TAG_VALUE: $type = $tag;
50        }
51
52        impl Default for $name {
53            fn default() -> Self {
54                Self(Self::TAG_VALUE)
55            }
56        }
57
58        impl BorshDeserialize for $name {
59            #[inline]
60            fn deserialize(buf: &mut &[u8]) -> std::io::Result<Self> {
61                let tag = <$type as BorshDeserialize>::deserialize(buf)?;
62                if tag != Self::TAG_VALUE {
63                    return Err(std::io::Error::new(
64                        std::io::ErrorKind::InvalidData,
65                        "invalid tag",
66                    ));
67                }
68                Ok($name(tag))
69            }
70        }
71    };
72}
73
74pub mod fee_owner {
75    use solana_program::declare_id;
76
77    declare_id!("3LTvJCPiPSMjX1kBJ6ZfEhn4G2hM46aJ1yEZsk8i12TK");
78}
79
80declare_tag!(PoolStateTag, u64, 0x16a7874c7fb2301b);
81
82#[derive(Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, BorshSchema)]
83pub struct PoolState {
84    pub tag: PoolStateTag,
85
86    /// Token mint account for the pool token.
87    pub pool_token_mint: Address,
88    /// Mint and vaults for the assets in the pool.
89    pub assets: Vec<AssetInfo>,
90
91    /// Mint authority for the pool token and owner for the assets in the pool.
92    pub vault_signer: Address,
93    /// Nonce used to generate `vault_signer`.
94    pub vault_signer_nonce: u8,
95
96    /// Additional accounts that need to be included with every request.
97    pub account_params: Vec<ParamDesc>,
98
99    /// User-friendly pool name.
100    pub name: String,
101
102    /// Vault for fees collected by the pool for LQD. Mint is the pool token mint.
103    pub lqd_fee_vault: Address,
104    /// Vault for fees collected by the pool for the pool initializer. Mint is the pool token mint.
105    pub initializer_fee_vault: Address,
106
107    /// Fee on creations and redemptions, per million tokens.
108    pub fee_rate: u32,
109
110    /// Meaning depends on the pool implementation.
111    pub admin_key: Option<Address>,
112
113    pub custom_state: Vec<u8>,
114}
115
116#[derive(Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, BorshSchema)]
117pub struct AssetInfo {
118    pub mint: Address,
119    /// Vault should be owned by `PoolState::vault_signer`
120    pub vault_address: Address,
121}
122
123#[derive(Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, BorshSchema)]
124pub struct ParamDesc {
125    pub address: Address,
126    pub writable: bool,
127}
128
129pub const MIN_FEE_RATE: u32 = 150;
130pub const DEFAULT_FEE_RATE: u32 = 2500;
131pub const FEE_RATE_DENOMINATOR: u32 = 1_000_000;
132
133declare_tag!(PoolRequestTag, u64, 0x220a6cbdcd1cc4cf);
134
135#[derive(Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, BorshSchema)]
136pub struct PoolRequest {
137    pub tag: PoolRequestTag,
138    pub inner: PoolRequestInner,
139}
140
141#[derive(Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, BorshSchema)]
142pub enum PoolRequestInner {
143    /// Initialize a pool.
144    ///
145    /// Accounts:
146    ///
147    /// - `[writable]` Pool account
148    /// - `[writable]` Pool token mint (`PoolState::pool_token_mint`)
149    /// - `[writable]` Pool vault account for each of the N pool assets (`AssetInfo::vault_address`)
150    /// - `[]` Pool vault authority (`PoolState::vault_signer`)
151    /// - `[]` LQD fee vault
152    /// - `[]` Initializer fee vault
153    /// - `[]` Rent sysvar
154    /// - `[]/[writable]` Any additional accounts needed to initialize the pool
155    Initialize(InitializePoolRequest),
156
157    /// Get the creation, redemption, or swap basket.
158    ///
159    /// Basket is written to the retbuf account as a Vec<i64>.
160    ///
161    /// Accounts:
162    ///
163    /// - `[]` Pool account
164    /// - `[]` Pool token mint (`PoolState::pool_token_mint`)
165    /// - `[]` Pool vault account for each of the N pool assets (`AssetInfo::vault_address`)
166    /// - `[]` Pool vault authority (`PoolState::vault_signer`)
167    /// - `[writable]` retbuf account
168    /// - `[]` retbuf program
169    /// - `[]` Accounts in `PoolState::account_params`
170    GetBasket(PoolAction),
171
172    /// Perform a creation, redemption, or swap.
173    ///
174    /// Accounts:
175    ///
176    /// - `[writable]` Pool account
177    /// - `[writable]` Pool token mint (`PoolState::pool_token_mint`)
178    /// - `[writable]` Pool vault account for each of the N pool assets (`AssetInfo::vault_address`)
179    /// - `[]` Pool vault authority (`PoolState::vault_signer`)
180    /// - `[writable]` User pool token account
181    /// - `[writable]` User account for each of the N pool assets
182    /// - `[signer]` Authority for user accounts
183    /// - `[writable]` LQD fee vault
184    /// - `[writable]` Initializer fee vault
185    /// - `[writable]` Referrer fee vault
186    /// - `[]` spl-token program
187    /// - `[]/[writable]` Accounts in `PoolState::account_params`
188    Execute(PoolAction),
189}
190
191#[derive(Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, BorshSchema)]
192pub struct InitializePoolRequest {
193    pub vault_signer_nonce: u8,
194    pub assets_length: u8,
195    pub pool_name: String,
196    pub fee_rate: u32,
197    pub custom_data: Vec<u8>,
198}
199
200#[derive(Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, BorshSchema)]
201pub enum PoolAction {
202    /// Create pool tokens by depositing assets into the pool.
203    Create(u64),
204    /// Redeem pool tokens by burning the token and receiving assets from the pool.
205    Redeem(u64),
206    /// Deposit assets into the pool and receive other assets from the pool.
207    Swap(Vec<u64>),
208}
209
210#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, BorshSchema)]
211pub struct Basket {
212    /// Must have the same length as `PoolState::assets`. Each item corresponds to
213    /// one of the assets in `PoolState::assets`.
214    pub quantities: Vec<i64>,
215}
216
217impl BorshSerialize for Address {
218    fn serialize<W: Write>(&self, writer: &mut W) -> io::Result<()> {
219        BorshSerialize::serialize(&self.0.to_bytes(), writer)
220    }
221}
222
223impl BorshDeserialize for Address {
224    fn deserialize(buf: &mut &[u8]) -> io::Result<Self> {
225        Ok(Self(Pubkey::new_from_array(BorshDeserialize::deserialize(
226            buf,
227        )?)))
228    }
229}
230
231impl BorshSchema for Address {
232    fn add_definitions_recursively(definitions: &mut HashMap<Declaration, Definition>) {
233        Self::add_definition(
234            Self::declaration(),
235            Definition::Struct {
236                fields: borsh::schema::Fields::UnnamedFields(vec![
237                    <[u8; 32] as BorshSchema>::declaration(),
238                ]),
239            },
240            definitions,
241        );
242        <[u8; 32] as BorshSchema>::add_definitions_recursively(definitions);
243    }
244
245    fn declaration() -> Declaration {
246        "Address".to_string()
247    }
248}