deposit_liquidity.rs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. use spl_math::uint::U256;
  2. use steel::*;
  3. use token_swap_api::prelude::*;
  4. pub fn process_deposit_liquidity(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult {
  5. // Load accounts.
  6. let [payer_info, depositor_info, pool_info, pool_authority_info, mint_liquidity_info, mint_a_info, mint_b_info, pool_account_a_info, pool_account_b_info, depositor_account_liquidity_info, depositor_account_a_info, depositor_account_b_info, token_program, system_program, associated_token_program] =
  7. accounts
  8. else {
  9. return Err(ProgramError::NotEnoughAccountKeys);
  10. };
  11. let args = DepositLiquidity::try_from_bytes(data)?;
  12. let amount_a = u64::from_le_bytes(args.amount_a);
  13. let amount_b = u64::from_le_bytes(args.amount_b);
  14. // Check payer account is signer and program is the correct program.
  15. payer_info.is_signer()?;
  16. token_program.is_program(&spl_token::ID)?;
  17. system_program.is_program(&system_program::ID)?;
  18. associated_token_program.is_program(&ASSOCIATED_TOKEN_PROGRAM_ID)?;
  19. // check if depositor is signer
  20. depositor_info.is_signer()?;
  21. // Verify mint_a and mint_b is a mint account.
  22. let _mint_a = mint_a_info.as_mint()?;
  23. let _mint_b = mint_b_info.as_mint()?;
  24. // validate pool account
  25. if pool_info.data_is_empty() {
  26. return Err(TokenSwapError::AccountIsNotExisted.into());
  27. }
  28. validate_pool_account(pool_info, *mint_a_info.key, *mint_b_info.key)?;
  29. let pool_info_data = pool_info.as_account_mut::<Pool>(&token_swap_api::ID)?;
  30. // validate pool authority
  31. validate_pool_authority(
  32. pool_info_data,
  33. pool_authority_info,
  34. *mint_a_info.key,
  35. *mint_b_info.key,
  36. )?;
  37. // validate mint liquidity
  38. validate_mint_liquidity(
  39. pool_info_data,
  40. mint_liquidity_info,
  41. *mint_a_info.key,
  42. *mint_b_info.key,
  43. )?;
  44. // // validate pool_account_a_info, pool_account_b_info
  45. let pool_account_a = pool_account_a_info
  46. .is_writable()?
  47. .as_associated_token_account(pool_authority_info.key, mint_a_info.key)?;
  48. let pool_account_b = pool_account_b_info
  49. .is_writable()?
  50. .as_associated_token_account(pool_authority_info.key, mint_b_info.key)?;
  51. // validate depositor_account_a_info and depositor_account_b_info
  52. let depositor_account_a = depositor_account_a_info
  53. .is_writable()?
  54. .as_associated_token_account(depositor_info.key, mint_a_info.key)?;
  55. let depositor_account_b = depositor_account_b_info
  56. .is_writable()?
  57. .as_associated_token_account(depositor_info.key, mint_b_info.key)?;
  58. // Prevent depositing assets the depositor does not own
  59. let mut amount_a = if amount_a > depositor_account_a.amount {
  60. depositor_account_a.amount
  61. } else {
  62. amount_a
  63. };
  64. let mut amount_b = if amount_b > depositor_account_b.amount {
  65. depositor_account_b.amount
  66. } else {
  67. amount_b
  68. };
  69. // Defining pool creation like this allows attackers to frontrun pool creation with bad ratios
  70. let pool_creation = pool_account_a.amount == 0 && pool_account_b.amount == 0;
  71. (amount_a, amount_b) = if pool_creation {
  72. // Add as is if there is no liquidity
  73. (amount_a, amount_b)
  74. } else {
  75. let ratio = U256::from(pool_account_a.amount)
  76. .checked_mul(U256::from(pool_account_b.amount))
  77. .unwrap();
  78. if pool_account_a.amount > pool_account_b.amount {
  79. (
  80. U256::from(amount_b).checked_mul(ratio).unwrap().as_u64(),
  81. amount_b,
  82. )
  83. } else {
  84. (
  85. amount_a,
  86. U256::from(amount_a).checked_div(ratio).unwrap().as_u64(),
  87. )
  88. }
  89. };
  90. // Transfer tokens to the pool
  91. transfer(
  92. depositor_info,
  93. depositor_account_a_info,
  94. pool_account_a_info,
  95. token_program,
  96. amount_a,
  97. )?;
  98. transfer(
  99. depositor_info,
  100. depositor_account_b_info,
  101. pool_account_b_info,
  102. token_program,
  103. amount_b,
  104. )?;
  105. // Computing the amount of liquidity about to be deposited
  106. let mut liquidity = U256::from(amount_a)
  107. .checked_mul(U256::from(amount_b))
  108. .unwrap()
  109. .integer_sqrt()
  110. .as_u64();
  111. // Lock some minimum liquidity on the first deposit
  112. if pool_creation {
  113. if liquidity < MINIMUM_LIQUIDITY {
  114. return Err(TokenSwapError::DepositTooSmall.into());
  115. }
  116. liquidity -= MINIMUM_LIQUIDITY;
  117. }
  118. if depositor_account_liquidity_info.data_is_empty() {
  119. // Create the depositor's liquidity account if it does not exist
  120. create_associated_token_account(
  121. payer_info,
  122. depositor_info,
  123. depositor_account_liquidity_info,
  124. mint_liquidity_info,
  125. system_program,
  126. token_program,
  127. associated_token_program,
  128. )?;
  129. }
  130. // Mint the liquidity to user
  131. let seeds = &[
  132. pool_info_data.amm.as_ref(),
  133. pool_info_data.mint_a.as_ref(),
  134. pool_info_data.mint_b.as_ref(),
  135. AUTHORITY_SEED,
  136. &[pool_info_data.pool_authority_bump],
  137. ];
  138. let signer_seeds = &[&seeds[..]];
  139. solana_program::program::invoke_signed(
  140. &spl_token::instruction::mint_to(
  141. &spl_token::id(),
  142. mint_liquidity_info.key,
  143. depositor_account_liquidity_info.key,
  144. pool_authority_info.key,
  145. &[pool_authority_info.key],
  146. liquidity,
  147. )?,
  148. &[
  149. token_program.clone(),
  150. mint_liquidity_info.clone(),
  151. depositor_account_liquidity_info.clone(),
  152. pool_authority_info.clone(),
  153. ],
  154. signer_seeds,
  155. )?;
  156. Ok(())
  157. }