Skip to content

Commit

Permalink
Fix liquidity deposit bug
Browse files Browse the repository at this point in the history
  • Loading branch information
Dodecahedr0x committed Nov 3, 2024
1 parent 0ed970d commit eacd6e9
Show file tree
Hide file tree
Showing 11 changed files with 805 additions and 2,899 deletions.
4 changes: 2 additions & 2 deletions Anchor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
seeds = false
skip-lint = false
[programs.localnet]
amm_tutorial = "4aTThYD7GtPg6cdipzdWCkDuj2KdePGLm9Goc8RyRBWn"
amm_tutorial = "9e2DFyoPwf9GNZ9oyGBpsh5W9FoT2CwUJ5USX9qVHMF8"

[registry]
url = "https://api.apr.dev"

[provider]
cluster = "Localnet"
wallet = "/Users/andrew/my-solana-wallet/wba-wallet.json"
wallet = "~/.config/solana/id.json"

[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
2,166 changes: 0 additions & 2,166 deletions package-lock.json

This file was deleted.

39 changes: 20 additions & 19 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
{
"scripts": {
"lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w",
"lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check"
},
"dependencies": {
"@coral-xyz/anchor": "^0.29.0",
"@project-serum/anchor": "^0.26.0",
"@solana/spl-token": "^0.3.8"
},
"devDependencies": {
"@types/bn.js": "^5.1.0",
"@types/chai": "^4.3.0",
"@types/mocha": "^9.0.0",
"chai": "^4.3.4",
"mocha": "^9.0.3",
"prettier": "^2.6.2",
"ts-mocha": "^10.0.0",
"typescript": "^4.3.5"
}
"scripts": {
"lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w",
"lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check"
},
"dependencies": {
"@coral-xyz/anchor": "^0.29.0",
"@project-serum/anchor": "^0.26.0",
"@solana/spl-token": "^0.3.8"
},
"devDependencies": {
"@types/bn.js": "^5.1.0",
"@types/chai": "^4.3.0",
"@types/mocha": "^9.0.0",
"chai": "^4.3.4",
"mocha": "^9.0.3",
"prettier": "^2.6.2",
"ts-mocha": "^10.0.0",
"typescript": "^4.3.5"
},
"packageManager": "[email protected]"
}
22 changes: 3 additions & 19 deletions programs/amm-tutorial/src/instructions/create_pool.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use anchor_lang::prelude::*;
use anchor_spl::{
associated_token::AssociatedToken,
token::{Mint, Token, TokenAccount},
token::{Mint, Token},
};

use crate::{
Expand All @@ -28,7 +28,7 @@ pub struct CreatePool<'info> {
],
bump,
)]
pub amm: Account<'info, Amm>,
pub amm: Box<Account<'info, Amm>>,

#[account(
init,
Expand All @@ -42,7 +42,7 @@ pub struct CreatePool<'info> {
bump,
constraint = mint_a.key() < mint_b.key() @ TutorialError::InvalidMint
)]
pub pool: Account<'info, Pool>,
pub pool: Box<Account<'info, Pool>>,

/// CHECK: Read only authority
#[account(
Expand Down Expand Up @@ -75,22 +75,6 @@ pub struct CreatePool<'info> {

pub mint_b: Box<Account<'info, Mint>>,

#[account(
init,
payer = payer,
associated_token::mint = mint_a,
associated_token::authority = pool_authority,
)]
pub pool_account_a: Box<Account<'info, TokenAccount>>,

#[account(
init,
payer = payer,
associated_token::mint = mint_b,
associated_token::authority = pool_authority,
)]
pub pool_account_b: Box<Account<'info, TokenAccount>>,

/// The account paying for all rents
#[account(mut)]
pub payer: Signer<'info>,
Expand Down
230 changes: 116 additions & 114 deletions programs/amm-tutorial/src/instructions/deposit_liquidity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,121 +12,121 @@ use crate::{
state::Pool,
};

impl<'info> DepositLiquidity<'info> {
pub fn deposit_liquidity(
&mut self,
amount_a: u64,
amount_b: u64,
bumps: &DepositLiquidityBumps
) -> Result<()> {
// Prevent depositing assets the depositor does not own
let mut amount_a = if amount_a > self.depositor_account_a.amount {
self.depositor_account_a.amount
} else {
amount_a
};
let mut amount_b = if amount_b > self.depositor_account_b.amount {
self.depositor_account_b.amount
} else {
amount_b
};

// Making sure they are provided in the same proportion as existing liquidity
let pool_a = &self.pool_account_a;
let pool_b = &self.pool_account_b;
// Defining pool creation like this allows attackers to frontrun pool creation with bad ratios
let pool_creation = pool_a.amount == 0 && pool_b.amount == 0;
(amount_a, amount_b) = if pool_creation {
// Add as is if there is no liquidity
(amount_a, amount_b)
} else {
let ratio = I64F64::from_num(pool_a.amount)
.checked_mul(I64F64::from_num(pool_b.amount))
.unwrap();
if pool_a.amount > pool_b.amount {
(
I64F64::from_num(amount_b)
.checked_mul(ratio)
.unwrap()
.to_num::<u64>(),
amount_b,
)
impl<'info> DepositLiquidity<'info> {
pub fn deposit_liquidity(
&mut self,
amount_a: u64,
amount_b: u64,
bumps: &DepositLiquidityBumps,
) -> Result<()> {
// Prevent depositing assets the depositor does not own
let mut amount_a = if amount_a > self.depositor_account_a.amount {
self.depositor_account_a.amount
} else {
(
amount_a,
I64F64::from_num(amount_a)
.checked_div(ratio)
.unwrap()
.to_num::<u64>(),
)
}
};

// Computing the amount of liquidity about to be deposited
let mut liquidity = I64F64::from_num(amount_a)
.checked_mul(I64F64::from_num(amount_b))
.unwrap()
.sqrt()
.to_num::<u64>();

// Lock some minimum liquidity on the first deposit
if pool_creation {
if liquidity < MINIMUM_LIQUIDITY {
return err!(TutorialError::DepositTooSmall);
amount_a
};
let mut amount_b = if amount_b > self.depositor_account_b.amount {
self.depositor_account_b.amount
} else {
amount_b
};

// Making sure they are provided in the same proportion as existing liquidity
let pool_a = &self.pool_account_a;
let pool_b = &self.pool_account_b;
// Defining pool creation like this allows attackers to frontrun pool creation with bad ratios
let pool_creation = pool_a.amount == 0 && pool_b.amount == 0;
(amount_a, amount_b) = if pool_creation {
// Add as is if there is no liquidity
(amount_a, amount_b)
} else {
let ratio = I64F64::from_num(pool_a.amount)
.checked_div(I64F64::from_num(pool_b.amount))
.unwrap();
if pool_a.amount > pool_b.amount {
(
I64F64::from_num(amount_b)
.checked_mul(ratio)
.unwrap()
.to_num::<u64>(),
amount_b,
)
} else {
(
amount_a,
I64F64::from_num(amount_a)
.checked_div(ratio)
.unwrap()
.to_num::<u64>(),
)
}
};

// Computing the amount of liquidity about to be deposited
let mut liquidity = I64F64::from_num(amount_a)
.checked_mul(I64F64::from_num(amount_b))
.unwrap()
.sqrt()
.to_num::<u64>();

// Lock some minimum liquidity on the first deposit
if pool_creation {
if liquidity < MINIMUM_LIQUIDITY {
return err!(TutorialError::DepositTooSmall);
}

liquidity -= MINIMUM_LIQUIDITY;
}

liquidity -= MINIMUM_LIQUIDITY;
// Transfer tokens to the pool
token::transfer(
CpiContext::new(
self.token_program.to_account_info(),
Transfer {
from: self.depositor_account_a.to_account_info(),
to: self.pool_account_a.to_account_info(),
authority: self.depositor.to_account_info(),
},
),
amount_a,
)?;
token::transfer(
CpiContext::new(
self.token_program.to_account_info(),
Transfer {
from: self.depositor_account_b.to_account_info(),
to: self.pool_account_b.to_account_info(),
authority: self.depositor.to_account_info(),
},
),
amount_b,
)?;

// Mint the liquidity to user
let authority_bump = bumps.pool_authority;
let authority_seeds = &[
&self.pool.amm.to_bytes(),
&self.mint_a.key().to_bytes(),
&self.mint_b.key().to_bytes(),
AUTHORITY_SEED.as_bytes(),
&[authority_bump],
];
let signer_seeds = &[&authority_seeds[..]];
token::mint_to(
CpiContext::new_with_signer(
self.token_program.to_account_info(),
MintTo {
mint: self.mint_liquidity.to_account_info(),
to: self.depositor_account_liquidity.to_account_info(),
authority: self.pool_authority.to_account_info(),
},
signer_seeds,
),
liquidity,
)?;

Ok(())
}

// Transfer tokens to the pool
token::transfer(
CpiContext::new(
self.token_program.to_account_info(),
Transfer {
from: self.depositor_account_a.to_account_info(),
to: self.pool_account_a.to_account_info(),
authority: self.depositor.to_account_info(),
},
),
amount_a,
)?;
token::transfer(
CpiContext::new(
self.token_program.to_account_info(),
Transfer {
from: self.depositor_account_b.to_account_info(),
to: self.pool_account_b.to_account_info(),
authority: self.depositor.to_account_info(),
},
),
amount_b,
)?;

// Mint the liquidity to user
let authority_bump = bumps.pool_authority;
let authority_seeds = &[
&self.pool.amm.to_bytes(),
&self.mint_a.key().to_bytes(),
&self.mint_b.key().to_bytes(),
AUTHORITY_SEED.as_bytes(),
&[authority_bump],
];
let signer_seeds = &[&authority_seeds[..]];
token::mint_to(
CpiContext::new_with_signer(
self.token_program.to_account_info(),
MintTo {
mint: self.mint_liquidity.to_account_info(),
to: self.depositor_account_liquidity.to_account_info(),
authority: self.pool_authority.to_account_info(),
},
signer_seeds
),
liquidity,
)?;

Ok(())
}
}
#[derive(Accounts)]
pub struct DepositLiquidity<'info> {
Expand All @@ -140,7 +140,7 @@ pub struct DepositLiquidity<'info> {
has_one = mint_a,
has_one = mint_b,
)]
pub pool: Account<'info, Pool>,
pub pool: Box<Account<'info, Pool>>,

/// CHECK: Read only authority
#[account(
Expand Down Expand Up @@ -174,14 +174,16 @@ pub struct DepositLiquidity<'info> {
pub mint_b: Box<Account<'info, Mint>>,

#[account(
mut,
init_if_needed,
payer = payer,
associated_token::mint = mint_a,
associated_token::authority = pool_authority,
)]
pub pool_account_a: Box<Account<'info, TokenAccount>>,

#[account(
mut,
init_if_needed,
payer = payer,
associated_token::mint = mint_b,
associated_token::authority = pool_authority,
)]
Expand Down
Loading

0 comments on commit eacd6e9

Please sign in to comment.