1use crate::constants::{
2 ATA_PROGRAM_ID, LAUNCHPAD_PROGRAM_ID, NEXUS_PROGRAM_ID,
3 PYTH_SOL_USD_PRICE_ACCOUNT, STAKING_WSOL_VAULT_ADDRESS, SYSTEM_PROGRAM_ID,
4 TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID,
5};
6use crate::nexus::pda::STAKING_CONFIG_ADDRESS;
7use solana_address::Address;
8
9#[derive(Clone, Debug)]
10pub struct BuildCreateTokenParams<'a> {
11 pub user: &'a Address,
12 pub payer: Option<&'a Address>,
13 pub coin_creator: &'a Address,
14 pub base_mint: &'a Address,
15 pub quote_mint: &'a Address,
16 pub partner: &'a Address,
17 pub platform_config: &'a Address,
19 pub quote_token_program: &'a Address,
20 pub creator_platform: &'a str,
21 pub creator_id: &'a str,
22 pub name: &'a str,
23 pub symbol: &'a str,
24 pub uri: &'a str,
25}
26
27#[must_use]
30pub fn build_create_token_instruction(
31 p: &BuildCreateTokenParams<'_>,
32) -> solana_instruction::Instruction {
33 let creator_hash =
34 crate::utils::creator_hash_from_id(p.creator_platform, p.creator_id);
35 let (bonding_curve, _) =
36 super::pda::find_bonding_curve_pda(p.base_mint, p.quote_mint);
37 let (base_vault, _) = crate::utils::find_associated_token_pda(
38 &bonding_curve,
39 p.base_mint,
40 &TOKEN_2022_PROGRAM_ID,
41 );
42 let (quote_vault, _) = crate::utils::find_associated_token_pda(
43 &bonding_curve,
44 p.quote_mint,
45 p.quote_token_program,
46 );
47 let (creator_fee_config, _) =
48 crate::nexus::pda::find_creator_fee_config_pda(
49 &creator_hash,
50 p.quote_mint,
51 );
52 let (partner_config, _) = crate::nexus::pda::find_partner_config_pda(
53 p.platform_config,
54 p.partner,
55 );
56 let (staking_reward_state, _) = crate::nexus::pda::find_reward_state_pda(
57 &STAKING_CONFIG_ADDRESS,
58 p.quote_mint,
59 );
60 let (staking_vault, _) = crate::utils::find_associated_token_pda(
61 &STAKING_CONFIG_ADDRESS,
62 p.quote_mint,
63 p.quote_token_program,
64 );
65 let (reward_accrual, _) =
68 super::generated::pdas::find_reward_accrual_pda(p.quote_mint);
69
70 let create = super::instructions::CreateToken {
71 user: *p.user,
72 payer: *p.payer.unwrap_or(p.user),
73 coin_creator: *p.coin_creator,
74 global_config: super::pda::GLOBAL_CONFIG_ADDRESS,
75 base_mint: *p.base_mint,
76 quote_mint: *p.quote_mint,
77 bonding_curve,
78 base_vault,
79 quote_vault,
80 creator_fee_config,
81 nexus_program: NEXUS_PROGRAM_ID,
82 nexus_global_config: crate::nexus::pda::GLOBAL_CONFIG_ADDRESS,
83 partner: crate::utils::partner_account(p.partner),
84 partner_config,
85 nexus_staking_config: STAKING_CONFIG_ADDRESS,
86 staking_reward_state,
87 reward_accrual,
88 staking_vault,
89 staking_wsol_vault: STAKING_WSOL_VAULT_ADDRESS,
90 wsol_reward_accrual:
91 super::generated::pdas::WSOL_REWARD_ACCRUAL_ADDRESS,
92 pyth_price_feed: PYTH_SOL_USD_PRICE_ACCOUNT,
93 base_token_program: TOKEN_2022_PROGRAM_ID,
94 quote_token_program: *p.quote_token_program,
95 wsol_token_program: TOKEN_PROGRAM_ID,
96 associated_token_program: ATA_PROGRAM_ID,
97 system_program: SYSTEM_PROGRAM_ID,
98 event_authority: super::pda::EVENT_AUTHORITY_ADDRESS,
99 program: LAUNCHPAD_PROGRAM_ID,
100 };
101 create.instruction(super::instructions::CreateTokenInstructionArgs {
102 platform_config: *p.platform_config,
103 creator_platform: p.creator_platform.to_owned(),
104 creator_id: p.creator_id.to_owned(),
105 creator_hash,
106 name: p.name.to_owned(),
107 symbol: p.symbol.to_owned(),
108 uri: p.uri.to_owned(),
109 })
110}
111
112#[cfg(test)]
113mod tests {
114 use solana_address::Address;
115
116 use super::{BuildCreateTokenParams, build_create_token_instruction};
117 use crate::constants::{
118 DEFAULT_PARTNER, TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID, WSOL_MINT,
119 };
120
121 const PARTNER_INDEX: usize = 12;
122
123 const TSLAX_MINT: Address =
125 solana_address::address!("XsDoVfqeBukxuZHWhdvWHBhgEHjGNst4MLodqsJHzoB");
126
127 fn sample_instruction(
128 partner: &Address,
129 quote_mint: &Address,
130 quote_token_program: &Address,
131 ) -> solana_instruction::Instruction {
132 let user = Address::new_from_array([1u8; 32]);
133 let base_mint = Address::new_from_array([2u8; 32]);
134 let platform_config = Address::new_from_array([4u8; 32]);
135 build_create_token_instruction(&BuildCreateTokenParams {
136 user: &user,
137 payer: None,
138 coin_creator: &user,
139 base_mint: &base_mint,
140 quote_mint,
141 partner,
142 platform_config: &platform_config,
143 quote_token_program,
144 creator_platform: "wallet",
145 creator_id: "11111111111111111111111111111111",
146 name: "Sample Token",
147 symbol: "SMPL",
148 uri: "https://example.com/meta.json",
149 })
150 }
151
152 fn wsol_instruction(partner: &Address) -> solana_instruction::Instruction {
153 sample_instruction(partner, &WSOL_MINT, &TOKEN_PROGRAM_ID)
154 }
155
156 #[test]
157 fn non_default_partner_is_marked_as_signer() {
158 let partner = Address::new_from_array([3u8; 32]);
159 let ix = wsol_instruction(&partner);
160
161 assert_eq!(ix.accounts[PARTNER_INDEX].pubkey, partner);
162 assert!(ix.accounts[PARTNER_INDEX].is_signer);
163
164 let matches =
165 ix.accounts.iter().filter(|m| m.pubkey == partner).count();
166 assert_eq!(matches, 1);
167 }
168
169 #[test]
170 fn default_partner_is_not_marked_as_signer() {
171 let ix = wsol_instruction(&DEFAULT_PARTNER);
172
173 assert_eq!(ix.accounts[PARTNER_INDEX].pubkey, DEFAULT_PARTNER);
174 assert!(!ix.accounts[PARTNER_INDEX].is_signer);
175 }
176
177 #[test]
179 fn no_account_is_derived_from_the_creator_and_the_quote_mint() {
180 let user = Address::new_from_array([1u8; 32]);
181 let ix = sample_instruction(
182 &DEFAULT_PARTNER,
183 &TSLAX_MINT,
184 &TOKEN_2022_PROGRAM_ID,
185 );
186
187 let (user_quote_ata, _) = crate::utils::find_associated_token_pda(
188 &user,
189 &TSLAX_MINT,
190 &TOKEN_2022_PROGRAM_ID,
191 );
192 assert!(!ix.accounts.iter().any(|m| m.pubkey == user_quote_ata));
193
194 let (staking_wsol_vault, _) = crate::utils::find_associated_token_pda(
196 &crate::nexus::pda::STAKING_CONFIG_ADDRESS,
197 &WSOL_MINT,
198 &TOKEN_PROGRAM_ID,
199 );
200 assert!(ix.accounts.iter().any(|m| m.pubkey == staking_wsol_vault));
201 }
202
203 #[test]
205 fn the_fee_paying_user_slot_is_writable() {
206 let ix = wsol_instruction(&DEFAULT_PARTNER);
207
208 assert!(ix.accounts[0].is_writable);
209 assert!(ix.accounts[0].is_signer);
210 }
211}