# LP Interactions

{% hint style="warning" %}

## Note

Your primary method should be through the [Flash Trade API](https://docs.flash.trade/flash-trade/flash-trade-protocol/build-on-flash/flash-sdk/broken-reference) the SDK is the secondary method.
{% endhint %}

## Creating LP Transactions

### Add Liquidity / Mint sFLP

<pre class="language-typescript" data-overflow="wrap"><code class="lang-typescript"><strong>const addLiquidityAndStake = async () => {
</strong>    const usdcInputAmount = new BN(1_000_000); // $1
    
    // this can be any other token available in the pool, for instance SOL, BTC and ETH
    const usdcCustody = POOL_CONFIG.custodies.find(c => c.symbol === 'USDC')!;
    const slippageBps: number = 800 // 0.8%
    let instructions: TransactionInstruction[] = []
    let additionalSigners: Signer[] = []

    await flashClient.loadAddressLookupTable(POOL_CONFIG)

    // flash-sdk version >= 2.31.6
    const { amount: minLpAmountOut, fee } = await flashClient.getAddLiquidityAmountAndFeeView(usdcInputAmount, POOL_CONFIG.poolAddress, usdcCustody.custodyAccount, POOL_CONFIG);

    const minLpAmountOutAfterSlippage = minLpAmountOut
        .mul(new BN(10 ** BPS_DECIMALS - slippageBps))
        .div(new BN(10 ** BPS_DECIMALS))

    const setCULimitIx = ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000 }) // addLiquidity

    const addLiquidityAndStakeData = await flashClient.addLiquidityAndStake('USDC', usdcInputAmount, minLpAmountOutAfterSlippage, POOL_CONFIG);
    instructions.push(...addLiquidityAndStakeData.instructions)
    additionalSigners.push(...addLiquidityAndStakeData.additionalSigners)

    const flpStakeAccountPK = PublicKey.findProgramAddressSync(
        [Buffer.from('stake'), flashClient.provider.publicKey.toBuffer(), POOL_CONFIG.poolAddress.toBuffer()],
        POOL_CONFIG.programId
    )[0]

    const refreshStakeInstruction = await flashClient.refreshStake('USDC', POOL_CONFIG, [flpStakeAccountPK])

    instructions.push(refreshStakeInstruction)

    const trxId = await flashClient.sendTransaction([setCULimitIx, ...instructions])

    console.log('addLiquidityAndStake trx :>> ', trxId);
}
</code></pre>

### Add Compounding Liquidity / Mint FLP

{% code overflow="wrap" %}

```typescript
const addCompoundingLiquidity = async () => {
    // USDC with its decimals 
    const usdcInputAmount = new BN(1_000_000); // $1
    
    // this can be any other token available in the pool, for instance SOL, BTC and ETH
    const usdcCustody = POOL_CONFIG.custodies.find(c => c.symbol === 'USDC')!;
    const slippageBps: number = 800 // 0.8%
    let instructions: TransactionInstruction[] = []
    let additionalSigners: Signer[] = []

    await flashClient.loadAddressLookupTable(POOL_CONFIG)

    // flash-sdk version >= 2.31.6
    const { amount: minLpAmountOut, fee } = await flashClient.getAddCompoundingLiquidityAmountAndFeeView(usdcInputAmount, POOL_CONFIG.poolAddress, usdcCustody.custodyAccount, POOL_CONFIG);

    const minLpAmountOutAfterSlippage = minLpAmountOut
        .mul(new BN(10 ** BPS_DECIMALS - slippageBps))
        .div(new BN(10 ** BPS_DECIMALS))

    const setCULimitIx = ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000 }) // addLiquidity

    const addCompoundingLiquidityData = await flashClient.addCompoundingLiquidity(
        usdcInputAmount,
        minLpAmountOutAfterSlippage,
        'USDC',
        usdcCustody.mintKey,
        POOL_CONFIG
    )

    instructions.push(...addCompoundingLiquidityData.instructions)
    additionalSigners.push(...addCompoundingLiquidityData.additionalSigners)

    const trxId = await flashClient.sendTransaction([setCULimitIx, ...instructions])

    console.log('addCompoundingLiquidity trx :>> ', trxId);
}
```

{% endcode %}

### Remove Liquidity / Burn sFLP

{% code overflow="wrap" %}

```typescript
const removeSflpLiquidity = async () => {
    const usdcCustody = POOL_CONFIG.custodies.find(c => c.symbol === 'USDC')!;
    const slippageBps: number = 800 // 0.8%
    let instructions: TransactionInstruction[] = []
    let additionalSigners: Signer[] = []

    await flashClient.loadAddressLookupTable(POOL_CONFIG)

    const flpStakeAccountPK = PublicKey.findProgramAddressSync(
        [Buffer.from('stake'), flashClient.provider.publicKey.toBuffer(), POOL_CONFIG.poolAddress.toBuffer()],
        POOL_CONFIG.programId
    )[0]

    const flpStakeAccount = await flashClient.program.account.flpStake.fetch(flpStakeAccountPK);

    const flpWithPendingAndActive =
        flpStakeAccount?.stakeStats.activeAmount.add(flpStakeAccount?.stakeStats.pendingActivation) ??
        BN_ZERO

    // flash-sdk version >= 2.31.6
    const { amount: minTokenAmountOut, fee } = await flashClient.getRemoveLiquidityAmountAndFeeView(flpWithPendingAndActive, POOL_CONFIG.poolAddress, usdcCustody.custodyAccount, POOL_CONFIG);

    const { instructions: unstakeInstantInstructions, additionalSigners: unstakeInstantAdditionalSigners } =
        await flashClient.unstakeInstant('USDC', flpWithPendingAndActive, POOL_CONFIG)

    const { instructions: withdrawStakeInstructions, additionalSigners: withdrawStakeAdditionalSigners } =
        await flashClient.withdrawStake(POOL_CONFIG, true, true)

    instructions.push(...unstakeInstantInstructions)
    additionalSigners.push(...unstakeInstantAdditionalSigners)

    instructions.push(...withdrawStakeInstructions)
    additionalSigners.push(...withdrawStakeAdditionalSigners)

    const minTokenAmountOutAfterSlippage = minTokenAmountOut
        .mul(new BN(10 ** BPS_DECIMALS - slippageBps))
        .div(new BN(10 ** BPS_DECIMALS))

    const removeLiquidityData = await flashClient.removeLiquidity(
        'USDC',
        flpWithPendingAndActive,
        minTokenAmountOutAfterSlippage,
        POOL_CONFIG
    )

    instructions.push(...removeLiquidityData.instructions)
    additionalSigners.push(...removeLiquidityData.additionalSigners)

    const setCULimitIx = ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000 }) // addLiquidity

    const trxId = await flashClient.sendTransaction([setCULimitIx, ...instructions])

    console.log('trx :>> ', trxId);
}
```

{% endcode %}

### Remove Compounding Liquidity / Burn FLP

{% code overflow="wrap" %}

```typescript
const removeFlpLiquidity = async () => {
    const usdcCustody = POOL_CONFIG.custodies.find(c => c.symbol === 'USDC')!;
    const slippageBps: number = 800 // 0.8%
    let instructions: TransactionInstruction[] = []
    let additionalSigners: Signer[] = []
    const usdcToken = POOL_CONFIG.tokens.find(t => t.symbol === 'USDC')!;

    await flashClient.loadAddressLookupTable(POOL_CONFIG)

    const account = getAssociatedTokenAddressSync(POOL_CONFIG.compoundingTokenMint, flashClient.provider.publicKey, true)

    const walletBalance = await flashClient.provider.connection.getTokenAccountBalance(account, 'processed')
    const compoundingTokenBalance = new BN(walletBalance.value.amount)

    // flash-sdk version >= 2.31.6
    const { amount: minTokenAmountOut, fee } = await flashClient.getRemoveCompoundingLiquidityAmountAndFeeView(compoundingTokenBalance, POOL_CONFIG.poolAddress, usdcCustody.custodyAccount, POOL_CONFIG);

    const minTokenAmountOutAfterSlippage = minTokenAmountOut
        .mul(new BN(10 ** BPS_DECIMALS - slippageBps))
        .div(new BN(10 ** BPS_DECIMALS))

    const removeCompoundingLiquidityData = await flashClient.removeCompoundingLiquidity(
        compoundingTokenBalance,
        minTokenAmountOutAfterSlippage,
        'USDC',
        usdcToken.mintKey,
        POOL_CONFIG,
        true
    )

    instructions.push(...removeCompoundingLiquidityData.instructions)
    additionalSigners.push(...removeCompoundingLiquidityData.additionalSigners)

    const setCULimitIx = ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000 }) // addLiquidity

    const trxId = await flashClient.sendTransaction([setCULimitIx, ...instructions])

    console.log('trx :>> ', trxId);
}
```

{% endcode %}

### Update FLP/sFLP Price

{% code overflow="wrap" %}

```typescript
const setLpTokenPrice = async () => {
   
    await flashClient.loadAddressLookupTable(POOL_CONFIG)

    let instructions: TransactionInstruction[] = []
    let additionalSigners: Signer[] = []
    const setCULimitIx = ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000 }) // setLpTokenPrice

     // flash-sdk version >= "3.1.10"
     const setLpTokenPriceData = await flashClient.setLpTokenPrice(POOL_CONFIG);

    instructions.push(...setLpTokenPriceData.instructions)
    additionalSigners.push(...setLpTokenPriceData.additionalSigners)

    const trxId = await flashClient.sendTransaction([setCULimitIx, ...instructions])

    console.log('setLpTokenPrice trx :>> ', trxId);
   
}
```

{% endcode %}

### Composing Program Calls for Add/Remove Liquidity

Solana transaction instructions have strict size limits, and current runtime constraints prevent using Address Lookup Tables (LUTs) within inner instructions. To work around this, split the Add/Remove Liquidity flow into two separate  instruction sequences that execute in order.

#### Overview

1. **Prepare LP Token Price**

   * Use the `setLpTokenPrice` instruction to update or initialise the price data for your pool's LP token.
   * This instruction must run before any liquidity operations that depend on the latest price.&#x20;

   Example : refer the above example of  `Update FLP/sFLP Price`
2. **Add or Remove Liquidity**

   * Invoke the `AddLiquidityAndStake` ,  `AddCompoudingLiquidity` or `RemoveLiquidity`  , `RemoveCompoudingLiquidity`   instruction in after the LP Token price instuction.
   * By separating the price‐setting logic, you avoid passing large lists of remaining accounts in a single instruction, keeping each instruction well under the size limit.

   Example : Add Compounding Liquidity instruction without  passing any remaining accounts   &#x20;

```rust
let cpi_program = ctx.accounts.perp_program.to_account_info();

let acl_context = Box::new(CpiContext::new(
    cpi_program.clone(),
    AddCompoundingLiquidity {
        owner: ctx.accounts.owner.to_account_info(),
        funding_account: ctx.accounts.funding_account.to_account_info(),
        compounding_token_account: ctx.accounts.compounding_token_account.to_account_info(),
        pool_compounding_lp_vault: ctx.accounts.pool_compounding_lp_vault.to_account_info(),
        transfer_authority: ctx.accounts.transfer_authority.to_account_info(),
        perpetuals: ctx.accounts.perpetuals.to_account_info(),
        pool: ctx.accounts.pool.to_account_info(),
        in_custody: ctx.accounts.in_custody.to_account_info(),
        in_custody_oracle_account: ctx.accounts.in_custody_oracle_account.to_account_info(),
        in_custody_token_account: ctx.accounts.in_custody_token_account.to_account_info(),
        reward_custody: ctx.accounts.reward_custody.to_account_info(),
        reward_custody_oracle_account: ctx.accounts.reward_custody_oracle_account.to_account_info(),
        lp_token_mint: ctx.accounts.lp_token_mint.to_account_info(),
        compounding_token_mint: ctx.accounts.compounding_token_mint.to_account_info(),
        token_program: ctx.accounts.token_program.to_account_info(),
        event_authority: ctx.accounts.event_authority.to_account_info(),
        program: ctx.accounts.perp_program.to_account_info(),
        ix_sysvar: ctx.accounts.ix_sysvar.to_account_info(),
        funding_mint: ctx.accounts.funding_mint.to_account_info(),
        funding_token_program: ctx.accounts.funding_token_program.to_account_info(),
    },
));

let acl_params = Box::new(AddCompoundingLiquidityParams {
    amount_in: params.amount_in,
    min_compounding_amount_out: params.min_compounding_amount_out,
});

let compounding_amount = perpetuals::cpi::add_compounding_liquidity(*acl_context, *acl_params)?.get();
```

### Get FLP/sFLP token prices

1\)  Using SDK (Advanced Integration)

<pre class="language-typescript" data-overflow="wrap"><code class="lang-typescript"><strong>const getLpTokenPrices = async () => {
</strong>    await flashClient.loadAddressLookupTable(POOL_CONFIG)
    const stakedLpPrice = await flashClient.getStakedLpTokenPrice(POOL_CONFIG.poolAddress, POOL_CONFIG); // sFLP price
    const compoundingLPTokenPrice = await flashClient.getCompoundingLPTokenPrice(POOL_CONFIG.poolAddress, POOL_CONFIG); // FLP price

    console.log('stakedLpPrice :>> ', stakedLpPrice);
    console.log('compoundingLPTokenPrice :>> ', compoundingLPTokenPrice);
}
</code></pre>

#### 2) Using API (Quick & Easy)

Query the following endpoint to get price data for all pools:

```
https://api.prod.flash.trade/earn-page/data
```

**✅ Response Format (Example)**

The response is a JSON object containing an array of `pools`, each representing a unique FLP pool. Here's what each key means:

| Field                | Description                                                   |
| -------------------- | ------------------------------------------------------------- |
| `poolAddress`        | The unique on-chain address of the liquidity pool             |
| `aum`                | Assets under management (in USD) for this pool                |
| `flpTokenSymbol`     | Symbol for the compounding FLP token (e.g., `FLP.1`, `FLP.2`) |
| `sFlpTokenSymbol`    | Symbol for the staked FLP token (e.g., `sFLP.1`, `sFLP.2`)    |
| `flpDailyApy`        | Daily APY for compounding FLP token (auto-reinvested)         |
| `flpWeeklyApy`       | Weekly APY for compounding FLP token                          |
| `flpWeeklyApr`       | Weekly APR for compounding FLP token (non-compounding)        |
| `sFlpDailyApr`       | Daily APR for staked FLP token (non-compounding)              |
| `sFlpPrice`          | Current price of staked FLP token                             |
| `flpPrice`           | Current price of compounding FLP token                        |
| `sFlpPriceWithYield` | Derived price of sFLP token if yield is included in valuation |

**🔎 How to Use**

To get token prices:

* Match the `flpTokenSymbol` (e.g., `"FLP.1"`) or `poolAddress` to identify your desired pool.
* Use `flpPrice` for the compounding FLP token and `sFlpPrice` for the staked version.

This is the easiest method for frontends and dashboards needing real-time pricing and yield data without setting up the SDK or querying the blockchain.
