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