> 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/revenue-interactions.md).

# Revenue Interactions

{% hint style="info" %}

#### **Three claimable balances, three calls:**

* **Staking rewards** — `collectTokenRewardWithAction` (your FAF staking yield).
* **Protocol revenue** — `collectRevenueWithAction` (a slice of protocol fees distributed to FAF stakers).
* **Referral rebates** — `collectRebateWithAction` (trading rebates for referrers / stakers).

Each pays out **0 and simply closes** if nothing is owed, so they're safe to call speculatively.
{% endhint %}

Set up `flashClient` and `poolConfig` as shown on the [Flash SDK v2 landing page](/flash-trade/flash-trade-protocol/build-on-flash/flash-sdk-v2.md#setting-up-the-client). The FAF (governance) token mint and the revenue/rebate token accounts live on `poolConfig`:

```ts
poolConfig.tokenMint           // FAF / governance staking mint
poolConfig.revenueTokenAccount // holds the revenue payout mint (read on-chain)
poolConfig.rebateTokenAccount  // holds the rebate payout mint (read on-chain)
```

### The flow model

The claim flows (`collect*WithAction`, `depositTokenStakeWithAction`, `withdrawTokenWithAction`) are program-driven: you submit one `*WithAction` transaction with `sendAndConfirmTransaction`, and the program runs the remaining steps automatically, closing a **receipt** account. You submit the transaction and poll the receipt until it closes. Everything on this page is signed by your `walletKeypair` (session keys apply only to trading).

### Stake FAF

Stakes FAF into your `token_stake` account. Stake level (your fee/rebate tier) and revenue eligibility derive from the staked amount.

```ts
import { BN } from '@coral-xyz/anchor'
import { getAssociatedTokenAddressSync, createAssociatedTokenAccountIdempotentInstruction } from '@solana/spl-token'
import { findTokenStakeDepositReceiptAddress } from '@flash_trade/flash-sdk-v2'

const stakeFaf = async (amount: BN) => {
  const owner = flashClient.wallet
  const fafMint = poolConfig.tokenMint
  const fundingAccount = getAssociatedTokenAddressSync(fafMint, owner)
  const [receipt] = findTokenStakeDepositReceiptAddress(owner, flashClient.programId)

  const res = await flashClient.depositTokenStakeWithAction({
    tokenMint: fafMint,
    fundingAccount,
    depositAmount: amount,
    token22: false, // true if FAF is a Token-2022 mint
  })
  // Ensure the funding ATA exists (idempotent).
  res.instructions.unshift(
    createAssociatedTokenAccountIdempotentInstruction(owner, fundingAccount, owner, fafMint),
  )

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

  // Wait for the settle step to close the receipt.
  await pollClosed(receipt) // see LP Interactions for the poller helper
}

await stakeFaf(new BN(1_000_000))
```

{% hint style="warning" %}
`depositTokenStakeWithAction` activates your `token_stake` for staking. If it's **already** active from a prior stake, this transaction fails — use unstake/withdraw against the existing stake rather than staking into it again. After the first stake, unstake/cancel/withdraw run against it directly.
{% endhint %}

### Unstake (begin the unlock)

Appends an entry to your `withdraw_request` array. The amount matures after the vault's `unlock_period`, after which you withdraw it. Sent with `sendAndConfirmErTransaction`, signed by your wallet keypair.

```ts
const ix = await flashClient.unstakeTokenRequestEr(new BN(500_000), flashClient.wallet)
await flashClient.sendAndConfirmErTransaction([ix], [walletKeypair]) // owner signs
```

### Cancel a pending unstake

```ts
const requestId = 0 // index into the withdraw_request array
const ix = await flashClient.cancelUnstakeTokenRequestEr(requestId, flashClient.wallet)
await flashClient.sendAndConfirmErTransaction([ix], [walletKeypair])
```

### Withdraw a matured unstake

Once a request has matured, settle it to return FAF to your ATA.

```ts
const fafAta = getAssociatedTokenAddressSync(poolConfig.tokenMint, flashClient.wallet)
const res = await flashClient.withdrawTokenWithAction({
  tokenMint: poolConfig.tokenMint,
  receivingTokenAccount: fafAta,
  withdrawRequestId: 0,   // the matured request's index
  token22: false,
})
await flashClient.sendAndConfirmTransaction(res.instructions, { additionalSigners: res.additionalSigners })
```

### Reading your stake & claimable balances

`TokenStakeAccount` exposes your stake level, lock table, and the three claimable balances:

```ts
const ts = await flashClient.erAccounts!.fetchTokenStake(flashClient.wallet) // live copy
// (falls back to flashClient.accounts.fetchTokenStake before your first stake)

ts.level                    // staking tier
ts.activeStakeAmount        // staked FAF
ts.rewardTokens             // accrued staking rewards
ts.unclaimedRevenueAmount   // pending protocol revenue share
ts.claimableRebateUsd       // pending referral rebate (USD, 6 decimals)

ts.getWithdrawableAmount()  // matured (withdrawable) unstake total
ts.getLockStatus()          // per-request: { requestId, lockedAmount, withdrawableAmount, timeRemaining, … }
ts.getRevenueEligibleAmount()
```

{% hint style="info" %}
Fetching `tokenStake` throws if the account doesn't exist (the wallet has never staked). Guard with a try/catch or check existence first.
{% endhint %}

### Claim staking rewards

```ts
const fafAta = getAssociatedTokenAddressSync(poolConfig.tokenMint, flashClient.wallet)
const res = await flashClient.collectTokenRewardWithAction({
  tokenMint: poolConfig.tokenMint,
  receivingTokenAccount: fafAta,
  token22: false,
})
res.instructions.unshift(
  createAssociatedTokenAccountIdempotentInstruction(
    flashClient.wallet, fafAta, flashClient.wallet, poolConfig.tokenMint,
  ),
)
await flashClient.sendAndConfirmTransaction(res.instructions, { additionalSigners: res.additionalSigners })
// poll findCollectTokenRewardReceiptAddress(owner, programId) until closed
```

### Claim protocol revenue

A slice of protocol fees is distributed to FAF stakers. The payout mint is whatever the pool's `revenueTokenAccount` holds — read it on-chain rather than assuming.

```ts
import { getMint, getAccount } from '@solana/spl-token'
import { findCollectRevenueReceiptAddress } from '@flash_trade/flash-sdk-v2'

const owner = flashClient.wallet

// Discover the revenue payout mint from the pool's revenue token account.
const revenueAcct = await getAccount(flashClient.connection, poolConfig.revenueTokenAccount)
const revenueMint = revenueAcct.mint
const receivingRevenueAccount = getAssociatedTokenAddressSync(revenueMint, owner)
const [receipt] = findCollectRevenueReceiptAddress(owner, flashClient.programId)

const res = await flashClient.collectRevenueWithAction({
  revenueTokenMint: revenueMint,
  receivingRevenueAccount,
  token22: false,
})
res.instructions.unshift(
  createAssociatedTokenAccountIdempotentInstruction(owner, receivingRevenueAccount, owner, revenueMint),
)
await flashClient.sendAndConfirmTransaction(res.instructions, { additionalSigners: res.additionalSigners })
await pollClosed(receipt) // see LP Interactions for the poller helper
```

{% hint style="info" %}
If no revenue is owed, the settle step pays 0 and just closes the receipt — the call is a safe no-op. Check `tokenStake.unclaimedRevenueAmount` first if you want to skip the transaction entirely.
{% endhint %}

### Claim referral rebates

Referrers and stakers accrue trading rebates. Same shape as revenue, reading the mint from `poolConfig.rebateTokenAccount` (typically USDC).

```ts
import { findCollectRebateReceiptAddress } from '@flash_trade/flash-sdk-v2'

const rebateAcct = await getAccount(flashClient.connection, poolConfig.rebateTokenAccount)
const rebateMint = rebateAcct.mint
const receivingTokenAccount = getAssociatedTokenAddressSync(rebateMint, flashClient.wallet)

const res = await flashClient.collectRebateWithAction({
  rebateTokenMint: rebateMint,
  receivingTokenAccount,
  token22: false,
})
res.instructions.unshift(
  createAssociatedTokenAccountIdempotentInstruction(
    flashClient.wallet, receivingTokenAccount, flashClient.wallet, rebateMint,
  ),
)
await flashClient.sendAndConfirmTransaction(res.instructions, { additionalSigners: res.additionalSigners })
// poll findCollectRebateReceiptAddress(owner, programId) until closed
```

### Link a referrer

To register the wallet under a referrer (so the referrer earns rebates on your trades), build the standalone `createReferral` instruction.

```ts
import { createReferral } from '@flash_trade/flash-sdk-v2'
import { PublicKey } from '@solana/web3.js'

const referrerWallet = new PublicKey('…') // the referrer's wallet
const ix = await createReferral(flashClient.program, referrerWallet, { owner: flashClient.wallet })
await flashClient.sendAndConfirmTransaction([ix])
```

This creates your `referral` PDA (`["referral", owner]`) and links it to the referrer's stake. Once linked, pass `Privilege.Referral` on your [trades](/flash-trade/flash-trade-protocol/build-on-flash/flash-sdk-v2/trader-interactions.md#privilege-accounts-referral-and-stake) so the rebate accrues.


---

# 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/revenue-interactions.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.
