From d8ad37d3578bca0e5bf346f04f934d57c50533a4 Mon Sep 17 00:00:00 2001 From: Chase Moran Date: Mon, 13 Sep 2021 13:17:45 -0400 Subject: [PATCH] Allow migration liquidity removal Change-Id: I9ec62ffa94b26ffd1776a2b76aa7586424a77b64 --- solana/migration/src/api.rs | 1 + solana/migration/src/api/add_liquidity.rs | 3 +- solana/migration/src/api/claim_shares.rs | 9 +- solana/migration/src/api/create_pool.rs | 1 - solana/migration/src/api/migrate_tokens.rs | 2 +- solana/migration/src/api/remove_liquidity.rs | 117 +++++++++++++++++++ solana/migration/src/instructions.rs | 54 ++++++++- solana/migration/src/lib.rs | 2 + solana/migration/src/wasm.rs | 28 +++++ 9 files changed, 205 insertions(+), 12 deletions(-) create mode 100644 solana/migration/src/api/remove_liquidity.rs diff --git a/solana/migration/src/api.rs b/solana/migration/src/api.rs index bb305adf3..d7e3b5f10 100644 --- a/solana/migration/src/api.rs +++ b/solana/migration/src/api.rs @@ -2,3 +2,4 @@ pub mod add_liquidity; pub mod claim_shares; pub mod create_pool; pub mod migrate_tokens; +pub mod remove_liquidity; diff --git a/solana/migration/src/api/add_liquidity.rs b/solana/migration/src/api/add_liquidity.rs index d9e24b19f..514d8f16c 100644 --- a/solana/migration/src/api/add_liquidity.rs +++ b/solana/migration/src/api/add_liquidity.rs @@ -78,7 +78,8 @@ pub fn add_liquidity( let to_tokens_in = if accs.from_mint.decimals > accs.to_mint.decimals { data.amount } else { - data.amount - (data.amount % 10u64.pow((accs.to_mint.decimals - accs.from_mint.decimals) as u32)) + data.amount + - (data.amount % 10u64.pow((accs.to_mint.decimals - accs.from_mint.decimals) as u32)) }; // Transfer out-tokens in diff --git a/solana/migration/src/api/claim_shares.rs b/solana/migration/src/api/claim_shares.rs index 1ba6d93e9..fa5f516f4 100644 --- a/solana/migration/src/api/claim_shares.rs +++ b/solana/migration/src/api/claim_shares.rs @@ -8,10 +8,7 @@ use crate::{ ShareMintDerivationData, ToCustodyTokenAccount, }, - types::{ - SplAccount, - SplMint, - }, + types::SplAccount, MigrationError::WrongMint, }; use borsh::{ @@ -31,7 +28,6 @@ use solitaire::{ #[derive(FromAccounts)] pub struct ClaimShares<'b> { pub pool: Mut>, - pub to_mint: Data<'b, SplMint, { AccountState::Initialized }>, pub from_token_custody: Mut>, pub share_mint: Mut>, @@ -51,9 +47,6 @@ pub fn claim_shares( accs: &mut ClaimShares, data: ClaimSharesData, ) -> Result<()> { - if *accs.to_mint.info().key != accs.pool.to { - return Err(WrongMint.into()); - } if accs.lp_share_acc.mint != *accs.share_mint.info().key { return Err(WrongMint.into()); } diff --git a/solana/migration/src/api/create_pool.rs b/solana/migration/src/api/create_pool.rs index 47a1066f4..f511e8f8a 100644 --- a/solana/migration/src/api/create_pool.rs +++ b/solana/migration/src/api/create_pool.rs @@ -18,7 +18,6 @@ use borsh::{ }; use solana_program::program::invoke_signed; use solitaire::{ - processors::seeded::Seeded, CreationLamports::Exempt, *, }; diff --git a/solana/migration/src/api/migrate_tokens.rs b/solana/migration/src/api/migrate_tokens.rs index 942cdddcf..0a697a2f3 100644 --- a/solana/migration/src/api/migrate_tokens.rs +++ b/solana/migration/src/api/migrate_tokens.rs @@ -95,7 +95,7 @@ pub fn migrate_tokens( )?; invoke_seeded(&transfer_ix, ctx, &accs.authority_signer, None)?; - // The share amount should be equal to the amount of from tokens an lp would be getting + // The out amount needs to be decimal adjusted let out_amount = if accs.from_mint.decimals > accs.to_mint.decimals { data.amount .checked_div(10u64.pow((accs.from_mint.decimals - accs.to_mint.decimals) as u32)) diff --git a/solana/migration/src/api/remove_liquidity.rs b/solana/migration/src/api/remove_liquidity.rs new file mode 100644 index 000000000..6bc9e5f25 --- /dev/null +++ b/solana/migration/src/api/remove_liquidity.rs @@ -0,0 +1,117 @@ +use crate::{ + accounts::{ + AuthoritySigner, + CustodySigner, + MigrationPool, + MigrationPoolDerivationData, + ShareMint, + ShareMintDerivationData, + ToCustodyTokenAccount, + ToCustodyTokenAccountDerivationData, + }, + types::{ + SplAccount, + SplMint, + }, + MigrationError::WrongMint, +}; +use borsh::{ + BorshDeserialize, + BorshSerialize, +}; +use solitaire::{ + processors::seeded::{ + invoke_seeded, + Seeded, + }, + *, +}; + +#[derive(FromAccounts)] +pub struct RemoveLiquidity<'b> { + pub pool: Mut>, + pub from_mint: Data<'b, SplMint, { AccountState::Initialized }>, + pub to_mint: Data<'b, SplMint, { AccountState::Initialized }>, + pub to_token_custody: Mut>, + pub share_mint: Mut>, + + pub to_lp_acc: Mut>, + pub lp_share_acc: Mut>, + pub custody_signer: CustodySigner<'b>, + pub authority_signer: AuthoritySigner<'b>, +} + +#[derive(BorshDeserialize, BorshSerialize, Default)] +pub struct RemoveLiquidityData { + pub amount: u64, +} + +pub fn remove_liquidity( + ctx: &ExecutionContext, + accs: &mut RemoveLiquidity, + data: RemoveLiquidityData, +) -> Result<()> { + if *accs.from_mint.info().key != accs.pool.from { + return Err(WrongMint.into()); + } + if *accs.to_mint.info().key != accs.pool.to { + return Err(WrongMint.into()); + } + if accs.lp_share_acc.mint != *accs.share_mint.info().key { + return Err(WrongMint.into()); + } + accs.to_token_custody.verify_derivation( + ctx.program_id, + &ToCustodyTokenAccountDerivationData { + pool: *accs.pool.info().key, + }, + )?; + accs.share_mint.verify_derivation( + ctx.program_id, + &ShareMintDerivationData { + pool: *accs.pool.info().key, + }, + )?; + accs.pool.verify_derivation( + ctx.program_id, + &MigrationPoolDerivationData { + from: accs.pool.from, + to: accs.pool.to, + }, + )?; + + // The out amount needs to be decimal adjusted + let out_amount = if accs.from_mint.decimals > accs.to_mint.decimals { + data.amount + .checked_div(10u64.pow((accs.from_mint.decimals - accs.to_mint.decimals) as u32)) + .unwrap() + } else { + data.amount + .checked_mul(10u64.pow((accs.to_mint.decimals - accs.from_mint.decimals) as u32)) + .unwrap() + }; + + // Transfer removed liquidity to LP + let transfer_ix = spl_token::instruction::transfer( + &spl_token::id(), + accs.to_token_custody.info().key, + accs.to_lp_acc.info().key, + accs.custody_signer.key, + &[], + out_amount, + )?; + invoke_seeded(&transfer_ix, ctx, &accs.custody_signer, None)?; + + // Burn LP shares + let mint_ix = spl_token::instruction::burn( + &spl_token::id(), + accs.lp_share_acc.info().key, + accs.share_mint.info().key, + accs.authority_signer.key, + &[], + data.amount, + )?; + invoke_seeded(&mint_ix, ctx, &accs.authority_signer, None)?; + + Ok(()) +} diff --git a/solana/migration/src/instructions.rs b/solana/migration/src/instructions.rs index 0b4d0ec5e..1cf77e0e8 100644 --- a/solana/migration/src/instructions.rs +++ b/solana/migration/src/instructions.rs @@ -16,6 +16,7 @@ use crate::{ claim_shares::ClaimSharesData, create_pool::CreatePoolData, migrate_tokens::MigrateTokensData, + remove_liquidity::RemoveLiquidityData, }, }; use borsh::BorshSerialize; @@ -83,6 +84,58 @@ pub fn add_liquidity( }) } +pub fn remove_liquidity( + program_id: Pubkey, + from_mint: Pubkey, + to_mint: Pubkey, + liquidity_token_account: Pubkey, + lp_share_token_account: Pubkey, + amount: u64, +) -> solitaire::Result { + let pool = MigrationPool::<'_, { AccountState::Initialized }>::key( + &MigrationPoolDerivationData { + from: from_mint, + to: to_mint, + }, + &program_id, + ); + Ok(Instruction { + program_id, + accounts: vec![ + AccountMeta::new(pool, false), + AccountMeta::new_readonly(from_mint, false), + AccountMeta::new_readonly(to_mint, false), + AccountMeta::new( + ToCustodyTokenAccount::<'_, { AccountState::Uninitialized }>::key( + &ToCustodyTokenAccountDerivationData { pool }, + &program_id, + ), + false, + ), + AccountMeta::new( + ShareMint::<'_, { AccountState::Uninitialized }>::key( + &ShareMintDerivationData { pool }, + &program_id, + ), + false, + ), + AccountMeta::new(liquidity_token_account, false), + AccountMeta::new(lp_share_token_account, false), + AccountMeta::new_readonly(CustodySigner::key(None, &program_id), false), + AccountMeta::new_readonly(AuthoritySigner::key(None, &program_id), false), + // Dependencies + AccountMeta::new(solana_program::sysvar::rent::id(), false), + AccountMeta::new(solana_program::system_program::id(), false), + AccountMeta::new_readonly(spl_token::id(), false), + ], + data: ( + crate::instruction::Instruction::RemoveLiquidity, + RemoveLiquidityData { amount }, + ) + .try_to_vec()?, + }) +} + pub fn claim_shares( program_id: Pubkey, from_mint: Pubkey, @@ -102,7 +155,6 @@ pub fn claim_shares( program_id, accounts: vec![ AccountMeta::new(pool, false), - AccountMeta::new_readonly(to_mint, false), AccountMeta::new( FromCustodyTokenAccount::<'_, { AccountState::Uninitialized }>::key( &FromCustodyTokenAccountDerivationData { pool }, diff --git a/solana/migration/src/lib.rs b/solana/migration/src/lib.rs index d0c95ef47..09475ade7 100644 --- a/solana/migration/src/lib.rs +++ b/solana/migration/src/lib.rs @@ -6,6 +6,7 @@ use api::{ claim_shares::*, create_pool::*, migrate_tokens::*, + remove_liquidity::*, }; use solitaire::{ solitaire, @@ -39,6 +40,7 @@ impl From for SolitaireError { solitaire! { AddLiquidity(AddLiquidityData) => add_liquidity, + RemoveLiquidity(RemoveLiquidityData) => remove_liquidity, ClaimShares(ClaimSharesData) => claim_shares, CreatePool(CreatePoolData) => create_pool, MigrateTokens(MigrateTokensData) => migrate_tokens, diff --git a/solana/migration/src/wasm.rs b/solana/migration/src/wasm.rs index 60a8eaab7..32495e464 100644 --- a/solana/migration/src/wasm.rs +++ b/solana/migration/src/wasm.rs @@ -50,6 +50,34 @@ pub fn add_liquidity( JsValue::from_serde(&ix).unwrap() } +#[wasm_bindgen] +pub fn remove_liquidity( + program_id: String, + from_mint: String, + to_mint: String, + liquidity_token_account: String, + lp_share_token_account: String, + amount: u64, +) -> JsValue { + let program_id = Pubkey::from_str(program_id.as_str()).unwrap(); + let from_mint = Pubkey::from_str(from_mint.as_str()).unwrap(); + let to_mint = Pubkey::from_str(to_mint.as_str()).unwrap(); + let liquidity_token_account = Pubkey::from_str(liquidity_token_account.as_str()).unwrap(); + let lp_share_token_account = Pubkey::from_str(lp_share_token_account.as_str()).unwrap(); + + let ix = instructions::remove_liquidity( + program_id, + from_mint, + to_mint, + liquidity_token_account, + lp_share_token_account, + amount, + ) + .unwrap(); + + JsValue::from_serde(&ix).unwrap() +} + #[wasm_bindgen] pub fn claim_shares( program_id: String,