Skip to content

Commit 0b3bfe3

Browse files
committed
Merge branch 'main' into test_branch
2 parents 0cf89c5 + 8d21ab1 commit 0b3bfe3

File tree

7 files changed

+123
-53
lines changed

7 files changed

+123
-53
lines changed

keys/test-kp.json

Lines changed: 0 additions & 1 deletion
This file was deleted.

programs/pump-science/src/errors.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,7 @@ pub enum ContractError {
8383

8484
#[msg("Invalid Fee Receiver")]
8585
InvalidFeeReceiver,
86+
87+
#[msg("Invalid Migration Authority")]
88+
InvalidMigrationAuthority,
8689
}

programs/pump-science/src/instructions/curve/swap.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,13 +141,15 @@ impl Swap<'_> {
141141
.apply_sell(exact_in_amount)
142142
.ok_or(ContractError::SellFailed)?;
143143

144+
msg!("SellResult: {:#?}", sell_result);
145+
144146
sol_amount = sell_result.sol_amount;
145147
token_amount = sell_result.token_amount;
148+
146149
let clock = Clock::get()?;
147150
fee_lamports = bonding_curve.calculate_fee(sol_amount, clock.unix_timestamp)?;
148-
149-
msg!("SellResult: {:#?}", sell_result);
150151
msg!("Fee: {} SOL", fee_lamports); // lamports to SOL
152+
151153
Swap::complete_sell(&ctx, sell_result.clone(), min_out_amount, fee_lamports)?;
152154
} else {
153155
// Buy tokens
@@ -157,14 +159,15 @@ impl Swap<'_> {
157159
.apply_buy(exact_in_amount)
158160
.ok_or(ContractError::BuyFailed)?;
159161

162+
msg!("BuyResult: {:#?}", buy_result);
163+
160164
sol_amount = buy_result.sol_amount;
161165
token_amount = buy_result.token_amount;
166+
162167
let clock = Clock::get()?;
163168
fee_lamports = bonding_curve.calculate_fee(sol_amount, clock.unix_timestamp)?;
164169
msg!("Fee: {} lamports", fee_lamports);
165170

166-
msg!("BuyResult: {:#?}", buy_result);
167-
168171
Swap::complete_buy(&ctx, buy_result.clone(), min_out_amount, fee_lamports)?;
169172

170173
let bonding_curve_total_lamports = ctx.accounts.bonding_curve.get_lamports();
@@ -195,12 +198,15 @@ impl Swap<'_> {
195198
locker.revoke_freeze_authority()?;
196199
}
197200
}
201+
198202
BondingCurve::invariant(
199203
&mut ctx
200204
.accounts
201205
.into_bonding_curve_locker_ctx(ctx.bumps.bonding_curve),
202206
)?;
203207
let bonding_curve = &ctx.accounts.bonding_curve;
208+
209+
// Emit trade event used for indexing
204210
emit_cpi!(TradeEvent {
205211
mint: *ctx.accounts.mint.to_account_info().key,
206212
sol_amount: sol_amount,
@@ -214,6 +220,8 @@ impl Swap<'_> {
214220
real_sol_reserves: bonding_curve.real_sol_reserves,
215221
real_token_reserves: bonding_curve.real_token_reserves,
216222
});
223+
224+
// Emit complete event when bonding curve is completed
217225
if bonding_curve.complete {
218226
emit_cpi!(CompleteEvent {
219227
user: *ctx.accounts.user.to_account_info().key,

programs/pump-science/src/instructions/migration/create_pool.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ pub struct InitializePoolWithConfig<'info> {
3434
pub vault: AccountInfo<'info>,
3535

3636
#[account(mut)]
37-
/// CHECK: Migration vault account where fee is deposited accounts
38-
pub migration_vault: UncheckedAccount<'info>,
37+
/// CHECK: fee receiver asserted in handler
38+
pub fee_receiver: AccountInfo<'info>,
3939

4040
#[account(mut)]
4141
/// CHECK: Pool account (PDA address)
@@ -105,8 +105,7 @@ pub struct InitializePoolWithConfig<'info> {
105105
/// CHECK: Protocol fee token b accounts
106106
pub protocol_token_b_fee: UncheckedAccount<'info>,
107107

108-
#[account(mut)]
109-
/// CHECK: Admin account
108+
#[account(mut, constraint = payer.key() == global.migration_authority @ ContractError::InvalidMigrationAuthority)]
110109
pub payer: Signer<'info>,
111110

112111
#[account(mut)]
@@ -162,6 +161,11 @@ pub fn initialize_pool_with_config(
162161
ContractError::NotCompleted
163162
);
164163

164+
require!(
165+
ctx.accounts.fee_receiver.key() == ctx.accounts.global.fee_receiver,
166+
ContractError::InvalidFeeReceiver
167+
);
168+
165169
let _clientbump = ctx.bumps.vault.to_le_bytes();
166170
let signer_seeds: &[&[&[u8]]] = &[
167171
&[VAULT_SEED, _clientbump.as_ref()]
@@ -260,9 +264,9 @@ pub fn pay_launch_fee(ctx: Context<InitializePoolWithConfig>) -> Result<()> {
260264
.bonding_curve
261265
.sub_lamports(fee_amount)
262266
.unwrap();
263-
ctx.accounts
264-
.migration_vault
265-
.add_lamports(fee_amount)
266-
.unwrap();
267+
ctx.accounts
268+
.fee_receiver
269+
.add_lamports(fee_amount)
270+
.unwrap();
267271
Ok(())
268272
}

programs/pump-science/src/instructions/migration/lock_pool.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,21 @@ use anchor_lang::prelude::*;
22
use anchor_lang::solana_program::{instruction::Instruction, program::invoke_signed};
33
use anchor_spl::associated_token;
44
use crate::constants::{VAULT_SEED, METEORA_PROGRAM_KEY};
5+
use crate::errors::ContractError;
6+
use crate::Global;
57
use std::str::FromStr;
68
use crate::state::meteora::{get_function_hash, get_lock_lp_ix_data};
79

8-
910
#[derive(Accounts)]
1011
pub struct LockPool<'info> {
11-
12+
#[account(
13+
mut,
14+
seeds = [Global::SEED_PREFIX.as_bytes()],
15+
constraint = global.initialized == true @ ContractError::NotInitialized,
16+
bump,
17+
)]
18+
global: Box<Account<'info, Global>>,
19+
1220
#[account(
1321
seeds = [VAULT_SEED],
1422
bump
@@ -55,8 +63,7 @@ pub struct LockPool<'info> {
5563
/// CHECK: Accounts to bootstrap the pool with initial liquidity
5664
pub payer_pool_lp: UncheckedAccount<'info>,
5765

58-
#[account(mut)]
59-
/// CHECK: Admin account
66+
#[account(mut, constraint = payer.key() == global.migration_authority @ ContractError::InvalidMigrationAuthority)]
6067
pub payer: Signer<'info>,
6168

6269
/// CHECK: Token program account

programs/pump-science/src/state/bonding_curve/curve.rs

Lines changed: 55 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,30 @@ impl BondingCurve {
9494
msg!("ApplyBuy: token_amount: {}", token_amount);
9595

9696
if token_amount >= self.real_token_reserves {
97+
// Last Buy
9798
token_amount = self.real_token_reserves;
99+
100+
// Temporarily store the current state
101+
let current_virtual_token_reserves = self.virtual_token_reserves;
102+
let current_virtual_sol_reserves = self.virtual_sol_reserves;
103+
104+
// Update self with the new token amount
105+
self.virtual_token_reserves = (current_virtual_token_reserves as u128)
106+
.checked_sub(token_amount as u128)?
107+
.try_into()
108+
.ok()?;
109+
self.virtual_sol_reserves = 115_005_359_056; // Total raise amount at end
110+
98111
let recomputed_sol_amount = self.get_sol_for_sell_tokens(token_amount)?;
99112
msg!("ApplyBuy: recomputed_sol_amount: {}", recomputed_sol_amount);
100113
sol_amount = recomputed_sol_amount;
114+
115+
// Restore the state with the recomputed sol_amount
116+
self.virtual_token_reserves = current_virtual_token_reserves;
117+
self.virtual_sol_reserves = current_virtual_sol_reserves;
118+
119+
// Set complete to true
120+
self.complete = true;
101121
}
102122

103123
// Adjusting token reserve values
@@ -199,8 +219,12 @@ impl BondingCurve {
199219
}
200220
msg!("GetTokensForBuySol: sol_amount: {}", sol_amount);
201221

202-
let product_of_reserves =
203-
(self.virtual_sol_reserves as u128).checked_mul(self.virtual_token_reserves as u128)?;
222+
// Calculate the product of the reserves (decimal adjusted)
223+
let product_of_reserves = ((self.virtual_sol_reserves as u128)
224+
.checked_div(1_000_000_000)?) // Divide by 9 decimals
225+
.checked_mul((self.virtual_token_reserves as u128).checked_div(1_000_000)?)? // Divide by 6 decimals
226+
.checked_mul(1_000_000_000)?; // Scaling factor
227+
204228
msg!(
205229
"GetTokensForBuySol: product_of_reserves: {}",
206230
product_of_reserves
@@ -213,7 +237,8 @@ impl BondingCurve {
213237
);
214238
let new_virtual_token_reserves = product_of_reserves
215239
.checked_div(new_virtual_sol_reserves)?
216-
.checked_add(1)?;
240+
.checked_mul(1_000_000)?; // Scale up to proper decimals again;
241+
217242
msg!(
218243
"GetTokensForBuySol: new_virtual_token_reserves: {}",
219244
new_virtual_token_reserves
@@ -227,33 +252,42 @@ impl BondingCurve {
227252
Some(recv)
228253
}
229254

230-
pub fn get_sol_for_sell_tokens(&self, tokens: u64) -> Option<u64> {
231-
msg!("get_sol_for_sell_tokens: tokens: {}", tokens);
232-
if tokens == 0 || tokens > self.virtual_token_reserves as u64 {
255+
pub fn get_sol_for_sell_tokens(&self, token_amount: u64) -> Option<u64> {
256+
if token_amount == 0 {
233257
return None;
234258
}
259+
msg!("GetSolForSellTokens: token_amount: {}", token_amount);
260+
261+
// Calculate the product of the reserves (decimal adjusted)
262+
let product_of_reserves = ((self.virtual_sol_reserves as u128)
263+
.checked_div(1_000_000_000)?) // Divide by 9 decimals
264+
.checked_mul((self.virtual_token_reserves as u128).checked_div(1_000_000)?)? // Divide by 6 decimals
265+
.checked_mul(1_000_000_000)?; // Scaling factor
235266

236-
let scaling_factor = self.initial_virtual_token_reserves as u128;
237267
msg!(
238-
"get_sol_for_sell_tokens: scaling_factor: {}",
239-
scaling_factor
268+
"GetSolForSellTokens: product_of_reserves: {}",
269+
product_of_reserves
240270
);
271+
let new_virtual_token_reserves =
272+
(self.virtual_token_reserves as u128).checked_add(token_amount as u128)?;
273+
msg!(
274+
"GetSolForSellTokens: new_virtual_token_reserves: {}",
275+
new_virtual_token_reserves
276+
);
277+
let new_virtual_sol_reserves = product_of_reserves
278+
.checked_div(new_virtual_token_reserves)?
279+
.checked_mul(1_000_000)?; // Scale up to proper decimals again;
241280

242-
let scaled_tokens = (tokens as u128).checked_mul(scaling_factor)?;
243-
msg!("get_sol_for_sell_tokens: scaled_tokens: {}", scaled_tokens);
244-
let token_sell_proportion =
245-
scaled_tokens.checked_div(self.virtual_token_reserves as u128)?;
246281
msg!(
247-
"get_sol_for_sell_tokens: token_sell_proportion: {}",
248-
token_sell_proportion
282+
"GetSolForSellTokens: new_virtual_sol_reserves: {}",
283+
new_virtual_sol_reserves
249284
);
250-
let sol_received = ((self.virtual_sol_reserves as u128)
251-
.checked_mul(token_sell_proportion)?)
252-
.checked_div(scaling_factor)?;
253-
msg!("get_sol_for_sell_tokens: sol_received: {}", sol_received);
254-
let recv = <u128 as std::convert::TryInto<u64>>::try_into(sol_received).ok()?;
285+
let sol_received =
286+
(self.virtual_sol_reserves as u128).checked_sub(new_virtual_sol_reserves)?;
287+
msg!("GetSolForSellTokens: sol_received: {}", sol_received);
255288

256-
msg!("get_sol_for_sell_tokens: recv: {}", recv);
289+
let recv = <u128 as std::convert::TryInto<u64>>::try_into(sol_received).ok()?;
290+
msg!("GetSolForSellTokens: recv: {}", recv);
257291
Some(recv)
258292
}
259293

programs/pump-science/src/state/bonding_curve/tests.rs

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ mod tests {
3939
println!("{:?} \n", buy_result);
4040

4141
assert_eq!(buy_result.token_amount, 793100000000000); // Max amount in curve
42-
assert_eq!(buy_result.sol_amount, 22174277726); // Should be max cost of curve
42+
assert_eq!(buy_result.sol_amount, 85007359056); // Should be max cost of curve
43+
assert_eq!(curve.complete, true);
4344
assert_eq!(
4445
curve.real_token_reserves,
4546
curve_initial.real_token_reserves - buy_result.token_amount
@@ -118,15 +119,15 @@ mod tests {
118119
let curve = bc.update_from_params(mint, creator, &global, &params, &CLOCK, 0);
119120

120121
// first apply buy
121-
curve.apply_buy(1000).unwrap();
122+
curve.apply_buy(1000000000).unwrap(); // 1 SOL
122123

123124
let curve_initial: BondingCurve = curve.clone();
124-
let sell_amount = 15766665; // Tokens
125+
let sell_amount = 14612904000000; // Tokens
125126

126127
let result = curve.apply_sell(sell_amount).unwrap();
127128
println!("{:?} \n", result);
128129
assert_eq!(result.token_amount, sell_amount);
129-
assert_eq!(result.sol_amount, 440); // Manually assert
130+
assert_eq!(result.sol_amount, 431000000); // Manually assert
130131
assert_eq!(
131132
curve.virtual_token_reserves,
132133
curve_initial.virtual_token_reserves + result.token_amount
@@ -161,12 +162,12 @@ mod tests {
161162
let curve = bc.update_from_params(mint, creator, &global, &params, &CLOCK, 0);
162163
let curve_initial = curve.clone();
163164

164-
let purchase_amount = 2000; // Lamports
165+
let purchase_amount = 1000000000; // 1 SOL
165166

166167
let result = curve.apply_buy(purchase_amount).unwrap();
167168
println!("{:?} \n", result);
168169
assert_eq!(result.sol_amount, purchase_amount);
169-
assert_eq!(result.token_amount, 71533328); // Manually assert
170+
assert_eq!(result.token_amount, 34612904000000); // Manually assert
170171
assert_eq!(
171172
curve.virtual_token_reserves,
172173
curve_initial.virtual_token_reserves - result.token_amount
@@ -197,14 +198,18 @@ mod tests {
197198
let mut bc = BondingCurve::default();
198199
let curve = bc.update_from_params(mint, creator, &global, &params, &CLOCK, 0);
199200

200-
// first apply buy
201-
curve.apply_buy(1000).unwrap();
201+
// first apply 1 SOL buy
202+
let buy_result = curve.apply_buy(1000000000).unwrap();
203+
println!("{:?} \n", buy_result);
202204

203205
// Edge case: zero tokens
204206
assert_eq!(curve.get_sol_for_sell_tokens(0), None);
205207

206208
// Normal case
207-
assert_eq!(curve.get_sol_for_sell_tokens(15766665), Some(440));
209+
assert_eq!(
210+
curve.get_sol_for_sell_tokens(34612904000000),
211+
Some(1001000000) // Slightly higher due to bonding curve bahaviour
212+
);
208213

209214
let real_sol_reserves = curve.real_sol_reserves;
210215
msg!("real_sol_reserves: {}", real_sol_reserves);
@@ -230,16 +235,26 @@ mod tests {
230235
start_time: Some(*START_TIME),
231236
};
232237
let mut bc = BondingCurve::default();
233-
let curve = bc.update_from_params(mint, creator, &global, &params, &CLOCK, 0);
238+
let mut curve = bc.update_from_params(mint, creator, &global, &params, &CLOCK, 0);
234239

235-
// Test case 1: Normal case
236-
assert_eq!(curve.get_tokens_for_buy_sol(100), Some(3576666)); // Adjusted based on current method logic
240+
// Test case 1: Normal case 0.01 SOL SOL
241+
assert_eq!(curve.get_tokens_for_buy_sol(10000000), Some(357548000000));
237242

238-
// Test case 2: Edge case - zero SOL
243+
// Test case 2: Normal case 1 SOL SOL
244+
curve = bc.update_from_params(mint, creator, &global, &params, &CLOCK, 0);
245+
assert_eq!(
246+
curve.get_tokens_for_buy_sol(1000000000),
247+
Some(34612904000000)
248+
);
249+
250+
// Test case 3: Edge case - zero SOL
239251
assert_eq!(curve.get_tokens_for_buy_sol(0), None);
240252

241-
// Test case 4: Large SOL amount (but within limits)
242-
assert_eq!(curve.get_tokens_for_buy_sol(3000), Some(107299989));
253+
// Test case 5: Large SOL amount (but within limits) - 50 SOL
254+
assert_eq!(
255+
curve.get_tokens_for_buy_sol(50000000000),
256+
Some(670625000000000) // 670M token about 66% of total supply
257+
);
243258

244259
// Test case 5: SOL amount that would exceed real token reserves
245260
// Check is made in apply_buy directly in order to recompute counter asset

0 commit comments

Comments
 (0)