# Withdraw

Removes liquidity from a pool on the CoolDEX.

## Instruction

```rust
WithdrawInstruction {
    amount: u64,
    min_coin_amount: Option<u64>,
    min_pc_amount: Option<u64>,
}
```

## Parameters

| Parameter         | Type          | Description                                                |
| ----------------- | ------------- | ---------------------------------------------------------- |
| `amount`          | `u64`         | Amount of LP tokens to burn                                |
| `min_coin_amount` | `Option<u64>` | Minimum acceptable coin token amount (slippage protection) |
| `min_pc_amount`   | `Option<u64>` | Minimum acceptable PC token amount (slippage protection)   |

## Account Setup

| #  | Account         | Description                      | Derivation                                                                |
| -- | --------------- | -------------------------------- | ------------------------------------------------------------------------- |
| 1  | Token program   | SPL Token program                | `TOKEN_PROGRAM_ID`                                                        |
| 2  | AMM info        | AMM account                      | `[program.toBuffer(), marketId.toBuffer(), "amm_associated_seed"]`        |
| 3  | AMM authority   | AMM authority PDA                | `[Buffer.from("amm authority")]`                                          |
| 4  | AMM LP mint     | LP token mint                    | `[program.toBuffer(), marketId.toBuffer(), "lp_mint_associated_seed"]`    |
| 5  | AMM coin vault  | Coin token vault                 | `[program.toBuffer(), marketId.toBuffer(), "coin_vault_associated_seed"]` |
| 6  | AMM PC vault    | PC token (SOL) vault             | `[program.toBuffer(), marketId.toBuffer(), "pc_vault_associated_seed"]`   |
| 7  | User source LP  | User's LP token account          | `getAssociatedTokenAddress(lpMint, userWallet, false)`                    |
| 8  | User dest coin  | User's coin token account        | `getAssociatedTokenAddress(coinMint, userWallet, false)`                  |
| 9  | User dest PC    | User's PC token (SOL) account    | `getAssociatedTokenAddress(NATIVE_MINT, userWallet, false)`               |
| 10 | Source LP owner | Transaction signer (User wallet) | -                                                                         |

## Function Logic

1. Verifies the transaction is signed by the LP token owner
2. Checks that the AMM is in a valid state for withdrawals
3. Verifies that the LP token amount is not zero or greater than the user's balance
4. Verifies that the LP token amount is less than the total supply (can't withdraw everything)
5. Calculates coin and PC amounts based on the proportion of LP tokens being burned
6. Verifies that calculated amounts meet minimum specified amounts (slippage check)
7. Transfers coin and PC tokens from respective vaults to the user
8. Burns the LP tokens
9. Updates the AMM's LP token supply

## Token Amount Calculation

The amounts of coin and PC tokens to withdraw are calculated using the proportion of LP tokens being burned:

```
coin_amount = (amount / amm.lp_amount) * total_coin_without_take_pnl
pc_amount = (amount / amm.lp_amount) * total_pc_without_take_pnl
```

Where:

* `amount` is the LP token amount being burned
* `amm.lp_amount` is the current total supply of LP tokens
* `total_coin_without_take_pnl` is the total coin amount in the pool
* `total_pc_without_take_pnl` is the total PC amount in the pool

## Notes

* Withdrawals always maintain the current ratio of assets in the pool
* LP tokens are burned in exchange for the underlying assets
* Slippage protection can be used to set minimum acceptable amounts
* Cannot withdraw the entire pool liquidity (must leave some LP tokens)
* Cannot withdraw if the calculated amounts exceed the vault balances

## Example Usage

```javascript
// Derive AMM accounts
const [ammAuthority] = PublicKey.findProgramAddressSync(
  [Buffer.from("amm authority")],
  COOLDEX_PROGRAM_ID
)

const [ammId] = PublicKey.findProgramAddressSync(
  [
    COOLDEX_PROGRAM_ID.toBuffer(),
    marketId.toBuffer(),
    Buffer.from("amm_associated_seed"),
  ],
  COOLDEX_PROGRAM_ID
)

const [lpMint] = PublicKey.findProgramAddressSync(
  [
    COOLDEX_PROGRAM_ID.toBuffer(),
    marketId.toBuffer(),
    Buffer.from("lp_mint_associated_seed"),
  ],
  COOLDEX_PROGRAM_ID
)

const [ammCoinVault] = PublicKey.findProgramAddressSync(
  [
    COOLDEX_PROGRAM_ID.toBuffer(),
    marketId.toBuffer(),
    Buffer.from("coin_vault_associated_seed"),
  ],
  COOLDEX_PROGRAM_ID
)

const [ammPcVault] = PublicKey.findProgramAddressSync(
  [
    COOLDEX_PROGRAM_ID.toBuffer(),
    marketId.toBuffer(),
    Buffer.from("pc_vault_associated_seed"),
  ],
  COOLDEX_PROGRAM_ID
)

// User token accounts
const userLpAccount = getAssociatedTokenAddressSync(lpMint, wallet.publicKey, false)

const userCoinAccount = getAssociatedTokenAddressSync(coinMint, wallet.publicKey, false)

const userWsolAccount = getAssociatedTokenAddressSync(
  NATIVE_MINT,
  wallet.publicKey,
  false
)

// Create instruction data
const data = Buffer.alloc(24)
data.writeUInt8(4, 0) // Withdraw instruction
data.writeBigUInt64LE(amount, 1) // LP token amount to burn
// Optionally add min_coin_amount and min_pc_amount if provided

const withdrawIx = new TransactionInstruction({
  programId: COOLDEX_PROGRAM_ID,
  keys: [
    { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
    { pubkey: ammId, isSigner: false, isWritable: true },
    { pubkey: ammAuthority, isSigner: false, isWritable: false },
    { pubkey: lpMint, isSigner: false, isWritable: true },
    { pubkey: ammCoinVault, isSigner: false, isWritable: true },
    { pubkey: ammPcVault, isSigner: false, isWritable: true },
    { pubkey: userLpAccount, isSigner: false, isWritable: true },
    { pubkey: userCoinAccount, isSigner: false, isWritable: true },
    { pubkey: userWsolAccount, isSigner: false, isWritable: true },
    { pubkey: wallet.publicKey, isSigner: true, isWritable: false },
  ],
  data: data,
})
```
