> For the complete documentation index, see [llms.txt](https://docs.flash.trade/flash-trade/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.flash.trade/flash-trade/flash-trade-protocol/build-on-flash/flash-sdk-v2.md).

# Flash SDK V2

### Introduction

Flash SDK v2 (`@flash_trade/flash-sdk-v2`) is the TypeScript client for Flash, covering trading, liquidity provisioning, and FAF staking with instant, gasless execution.

{% hint style="info" %}
**Use Flash SDK v2 for all new integrations.** The older [Flash SDK](/flash-trade/flash-trade-v1-deprecated/flash-sdk.md) (`flash-sdk`) is being deprecated and will be retired shortly — new builds should target v2.
{% endhint %}

### How it works

You give the client two RPC endpoints and call the instruction builders:

* A standard **Solana RPC** — bring your own from a provider like [Triton](https://triton.one/) or [Helius](https://helius.dev/).
* The **Flash `ER_RPC`** — Flash's trading endpoint, provided by Flash; trading and order execution run here.

There are two kinds of operations, and the rest of these docs are organized around them:

* **Trading** — open/close positions, adjust collateral, and place or edit orders. Each is built and sent in a single transaction with `sendAndConfirmErTransaction`.
* **Setup, deposits, liquidity, and staking** — sent with `sendAndConfirmTransaction`. The liquidity and staking operations are multi-step flows: you submit a transaction, then poll a **receipt** for the result (covered on each page).

### Install

```bash
npm install @flash_trade/flash-sdk-v2
# or
yarn add @flash_trade/flash-sdk-v2
```

The SDK already depends on `@coral-xyz/anchor`, `@solana/web3.js`, and `@solana/spl-token`, so they're installed for you. The examples here import directly from those three (for `BN`, `PublicKey`, `Keypair`, ATA helpers, etc.) — if you do the same, it's good practice to add them to your own `package.json` so the versions stay pinned and types resolve cleanly under strict package managers (pnpm, Yarn PnP):

```bash
npm install @coral-xyz/anchor @solana/web3.js @solana/spl-token
```

The main export is **`FlashPerpetualsClient`**. Everything you call hangs off one instance of it.

### Setting up the client

```ts
constructor(
  provider: AnchorProvider,
  idl?: any,                 // defaults to the bundled IDL — pass undefined
  programId?: PublicKey,     // PROGRAM_ID[cluster]
  opts?: FlashPerpetualsClientOptions,
  erEndpoint?: string,       // the Flash ER_RPC — required for trading & liquidity
)

type FlashPerpetualsClientOptions = {
  prioritizationFee?: number       // micro-lamports, default 0
  useExternalOracle?: boolean      // default false
  postSendTxCallback?: (args: { txid: string }) => void
  txConfirmationCommitment?: Commitment   // default "processed"
}
```

Create a single shared module and import the client (and your keypair) everywhere:

```ts
// flashClient.ts
import { AnchorProvider, Wallet } from '@coral-xyz/anchor'
import { Connection, Keypair } from '@solana/web3.js'
import { FlashPerpetualsClient, PoolConfig, PROGRAM_ID, type Cluster } from '@flash_trade/flash-sdk-v2'

const CLUSTER: Cluster = 'mainnet-beta' // or 'devnet'
const RPC_URL = process.env.RPC_URL!     // your Solana RPC (Triton, Helius, …)
const ER_RPC  = process.env.ER_RPC!      // Flash's ER endpoint

// Load your keypair however you like (Node example shown).
export const walletKeypair = Keypair.fromSecretKey(/* … */)

const connection = new Connection(RPC_URL, 'confirmed')
const provider = new AnchorProvider(connection, new Wallet(walletKeypair), {
  commitment: 'confirmed',
})

export const poolConfig = PoolConfig.fromIdsByName('Crypto.1', CLUSTER) // devnet: 'devnet.1'

export const flashClient = new FlashPerpetualsClient(
  provider,
  undefined,            // use the bundled IDL
  PROGRAM_ID[CLUSTER],  // program id for the cluster
  { prioritizationFee: 5000 },
  ER_RPC,
)
```

```env
RPC_URL=https://your-solana-rpc.example.com    # your Solana RPC (Triton, Helius, …)
ER_RPC=https://flash.magicblock.xyz            # mainnet  (devnet: https://devnet-as.magicblock.app)
```

### Sending transactions

You build instructions with the client, then send them one of two ways:

* `flashClient.sendAndConfirmTransaction(ixs, opts?)` — for one-time setup, deposits, liquidity, and staking. `opts` accepts `{ additionalSigners, skipPreflight, alts }`.
* `flashClient.sendAndConfirmErTransaction(ixs, signers)` — for trading (positions and orders) and for the commit step of liquidity/staking flows. `signers[0]` is the fee payer and signs the transaction — your **owner keypair** for a bot, or a [session key](#session-keys-optional-for-uis) if you've set one up.

Large setup/liquidity transactions forward a big account set inline — attach the pool's address lookup tables so they fit:

```ts
const alts = []
for (const addr of poolConfig.addressLookupTableAddresses ?? []) {
  const res = await flashClient.connection.getAddressLookupTable(addr)
  if (res.value) alts.push(res.value)
}

await flashClient.sendAndConfirmTransaction(instructions, { additionalSigners, skipPreflight: true, alts })
```

### Session keys (optional — for UIs)

If you're building a **UI**, a session key lets users trade without a wallet pop-up on every transaction: authorize a fresh keypair once, then sign trades with it locally.

{% hint style="info" %}
**Bots and scripts don't need session keys.** Sign trades directly with your owner keypair (`sendAndConfirmErTransaction(ixs, [walletKeypair])`). Session keys only affect trading instructions; everything else is always signed by the owner.
{% endhint %}

```ts
import { Keypair } from '@solana/web3.js'
import { BN } from '@coral-xyz/anchor'

// Authorize a session key (both the wallet and the session key sign this tx).
const sessionKeypair = Keypair.generate()
const validUntil = new BN(Math.floor(Date.now() / 1000) + 7 * 24 * 60 * 60) // ~7 days
const { instructions } = await flashClient.createSession(sessionKeypair.publicKey, false, validUntil)
await flashClient.sendAndConfirmTransaction(instructions, { additionalSigners: [sessionKeypair] })

// Activate it: trading instructions are now built for the session key.
flashClient.useSession(sessionKeypair.publicKey)
// Send trade txs with [sessionKeypair] as the signer. useSession(null) reverts to the owner.

// Close it on-chain and reclaim rent:
const { instructions: revokeIxs } = await flashClient.revokeSession(sessionKeypair.publicKey)
await flashClient.sendAndConfirmTransaction(revokeIxs)
```

### Receipts: how liquidity & staking flows report their outcome

Liquidity and staking operations return a **receipt** account. After your transaction confirms, the program runs the remaining steps and closes the receipt. Two helpers wait on it:

```ts
const outcome = await flashClient.awaitOutcome('compoundingDepositReceipt', receipt)
// outcome.status === 'settled' (with outcome.outAmount) | 'reverted' | 'timeout'

const status = await flashClient.awaitClosed(escrowPda) // 'closed' | 'timeout'
```

{% hint style="warning" %}
A **confirmed transaction is not a successful action.** On a slippage/price/cap miss the program refunds you and the transaction still *succeeds* — only the receipt tells you whether the mint/burn actually happened. Always read the receipt outcome before updating balances. [LP Interactions](/flash-trade/flash-trade-protocol/build-on-flash/flash-sdk-v2/lp-interactions.md) shows the full pattern.
{% endhint %}

### Reading accounts

Fetch typed account wrappers. Position/pool/custody reads use `client.accounts`; your **basket** (which holds your open positions and orders) is read from `client.erAccounts`:

```ts
const pool   = await flashClient.accounts.fetchPool('Crypto.1')               // PoolAccount
const basket = await flashClient.erAccounts!.fetchBasket(walletKeypair.publicKey) // BasketAccount
const stake  = await flashClient.accounts.fetchTokenStake(walletKeypair.publicKey) // TokenStakeAccount
```

Wrappers expose decoded fields directly (e.g. `pool.lpSupply`, `position.sizeAmount`) plus `.publicKey` and helpers. Your open positions and orders are **embedded in the basket** (`basket.positions`, `basket.orders`) — there's no separate per-position account. See [Trader Interactions → Reading positions & orders](/flash-trade/flash-trade-protocol/build-on-flash/flash-sdk-v2/trader-interactions.md#reading-positions-and-orders).

To stream updates, subscribe to the basket with `onAccountChange` and decode with the program coder:

```ts
import { BasketAccount, findBasketAddress } from '@flash_trade/flash-sdk-v2'

const [basketPk] = findBasketAddress(walletKeypair.publicKey, flashClient.programId)
const subId = flashClient.erConnection!.onAccountChange(basketPk, (info) => {
  const decoded = flashClient.erProgram.coder.accounts.decode('basket', info.data)
  const basket = BasketAccount.from(basketPk, decoded)
  // …react to basket.positions / basket.orders
}, { commitment: 'processed' })
// flashClient.erConnection!.removeAccountChangeListener(subId)
```

### Quotes & views

Pricing math runs on-chain — the SDK simulates it and decodes the typed result, so you never re-implement fee/PnL math. Views live under `flashClient.views.*`:

```ts
// collateral is fixed by the market — resolve it, don't hardcode it (a wrong
// collateral symbol makes the market PDA mismatch → ConstraintSeeds).
const { market, collateralSymbol } = getMarket('SOL', Side.Long)
const q = await flashClient.views.getOpenPositionQuoteEr(poolConfig, {
  market, targetSymbol: 'SOL', collateralSymbol,
  receivingSymbol: collateralSymbol, amountIn, leverage: new BN(20000), // 2x (BPS_DECIMALS = 4)
})
```

Each interaction page lists the views relevant to it.

### Troubleshooting

On-chain failures come back as `{"InstructionError":[i,{"Custom":N}]}`. Decode `N` by range: `2000`–`2999` are **Anchor** framework constraints; `≥ 6000` are the **perpetuals program's** own errors (offset from 6000); small codes like `0x1` are **SPL Token**; `0x0` from the System program means an account already exists.

| Error                                             | Cause                                                                                                                                                                       | Fix                                                                                                                                                                                                                                             |
| ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Custom 2006` — Anchor `ConstraintSeeds`          | A passed PDA doesn't match what the program re-derives — most often the **wrong collateral symbol for the market** (the market PDA is keyed by target + collateral + side). | Resolve the market and use *its* `collateralSymbol` ([Resolving the market](/flash-trade/flash-trade-protocol/build-on-flash/flash-sdk-v2/trader-interactions.md#resolving-the-market)); don't hardcode. Confirm cluster/pool/program id agree. |
| `Custom 6023` — `MinLeverage`                     | `sizeAmount` is in **target-token base units**, not USD, so a USD-looking number lands far below the minimum leverage.                                                      | Derive `sizeAmount` from `getOpenPositionQuoteEr` at your target `leverage` (BPS) instead of passing a raw size.                                                                                                                                |
| `Custom 6021` — `MaxLeverage`                     | Same size↔collateral unit confusion, other direction.                                                                                                                       | Same fix — derive the size from a quote.                                                                                                                                                                                                        |
| SPL Token `0x1` — insufficient funds              | The funding account is empty/underfunded; for trades, collateral isn't in the deposit ledger yet.                                                                           | Fund the ATA, and run the one-time [setup](/flash-trade/flash-trade-protocol/build-on-flash/flash-sdk-v2/trader-interactions.md#one-time-setup) to deposit collateral before trading.                                                           |
| System `0x0` — account in use                     | A receipt from a half-finished liquidity/staking flow is still open.                                                                                                        | Resume that receipt rather than resubmitting — see [Reading the outcome / resuming](/flash-trade/flash-trade-protocol/build-on-flash/flash-sdk-v2/lp-interactions.md#reading-the-outcome-resuming).                                             |
| Account-not-found on `getPnl` / `getPositionData` | The base `getPnl` / `getPositionData` views target the old standalone position model, not the basket.                                                                       | Use the `*Er` variants — your positions live in the basket.                                                                                                                                                                                     |

{% hint style="warning" %}
**A confirmed transaction is not a successful action** for liquidity/staking flows — always read the receipt outcome before trusting balances.
{% endhint %}

### Next steps

* [**Trader Interactions**](/flash-trade/flash-trade-protocol/build-on-flash/flash-sdk-v2/trader-interactions.md) — basket setup, open/close/modify positions, limit & trigger orders, quotes, reading positions.
* [**LP Interactions**](/flash-trade/flash-trade-protocol/build-on-flash/flash-sdk-v2/lp-interactions.md) — add/remove liquidity (FLP & sFLP), migrations, and the receipt-driven flow.
* [**Revenue Interactions**](/flash-trade/flash-trade-protocol/build-on-flash/flash-sdk-v2/revenue-interactions.md) — FAF staking, claiming staking rewards, protocol revenue, and referral rebates.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.flash.trade/flash-trade/flash-trade-protocol/build-on-flash/flash-sdk-v2.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
