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

# Setup

> Verified against the public fastpoker IDL (PokerXYdXL2SKNnfGbv1WE7vJHipTpNsfZbZeVvoJLn) released 2026-07-17.

**Capability: Wallet + RPC only**

> Beta software on mainnet. Examples can fail. Test with small amounts.

## Purpose

This page bootstraps every later integration. It installs the two libraries you need, opens a `Connection`, loads a wallet, and creates one shared module, `fastpoker.ts`. That module holds the program IDs, the fastpoker IDL import, the PDA derivation helpers, and a `sendAndConfirm` helper. Later pages import from `./setup.md` instead of redeclaring these values.

The PDA helpers and program IDs here are byte-exact with the on-chain `seeds = [...]` arrays and the IDL `address`. Do not edit the seed strings or the program IDs.

## When to use / who signs

Use this page first. Read it before any other integration page.

No transaction is sent here. You only need a wallet for the `feePayer` and signer roles used by the helper. Read-only flows (deriving PDAs, fetching account data) need a `PublicKey` only. Signing flows (joining, acting, settling) need a `Signer` or a wallet adapter that can sign and supply a public key.

## Inputs and constants

Install the two libraries.

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

You need an RPC endpoint. Mainnet flows use a Solana L1 RPC. Live gameplay also reaches a MagicBlock ephemeral rollup endpoint, covered on its own page. For setup you only need the L1 RPC.

### Program IDs

| Program                         | Pubkey                                         |
| ------------------------------- | ---------------------------------------------- |
| FastPoker (main)                | `PokerXYdXL2SKNnfGbv1WE7vJHipTpNsfZbZeVvoJLn`  |
| fastpoker\_registry             | `pokerQBdo685uLSkpVSyZ1vWooPYYTUhGkeKAHyCmax`  |
| Permission program (MagicBlock) | `ACLseoPoyC3cBqoUtkbjZ4aDrkurZW86v19pXz2XQnp1` |
| Steel tokenomics program        | `FASTPjXb68fPW9JRYSBS3EDoaT6inz84GoqkPK52dsA9` |
| MagicBlock Delegation program   | `DELeGGvXpWV2fqJUhqcF5ZSYMS4JTLjteaAMARRSaeSh` |

### PDA seeds used by the helper

All PDAs derive against the FastPoker program ID. The `table_id` is a 32-byte array. `seat_index`, `game_type`, and `tier` are single bytes. `page_index` is a `u16` little-endian. `match_id` and `start_hand` are `u64` little-endian.

| PDA                 | Seeds                                      |
| ------------------- | ------------------------------------------ |
| Table               | `["table", table_id]`                      |
| Seat                | `["seat", table, seat_index]`              |
| SeatCards           | `["seat_cards", table, seat_index]`        |
| DeckState           | `["deck_state", table]`                    |
| Player              | `["player", wallet]`                       |
| TableVault (cash)   | `["vault", table]`                         |
| SngPool             | `["sng_pool", game_type, tier]`            |
| SngPoolVault        | `["sng_pool_vault", game_type, tier]`      |
| SngMatch            | `["sng_match", sng_pool, match_id]`        |
| SngQueueMarker      | `["sng_queue_marker", sng_pool, player]`   |
| SngQueuePage        | `["sng_queue_page", sng_pool, page_index]` |
| SngDuelState        | `["sng_duel", table]`                      |
| SngSettlementRecord | `["sng_settlement", table, start_hand]`    |
| EmissionCtrl        | `["emission_ctrl"]`                        |
| PoolIdle            | `["pool_idle", game_type, tier]`           |

For the full seed list and account layouts, see [../04-architecture/state-accounts.md](/architecture/state-accounts.md). For the 21 SNG pool derivations, see [deriving-pools.md](/building-on-the-protocol/deriving-pools.md). For raw byte offsets and decoding, see [reading-accounts.md](/building-on-the-protocol/reading-accounts.md).

### GameType enum

| Value | Variant         |
| ----- | --------------- |
| 0     | SitAndGoHeadsUp |
| 1     | SitAndGo6Max    |
| 2     | SitAndGo9Max    |
| 3     | CashGame        |

### SNG tiers

Fee is 10% of the total buy-in. Prize is 90%. Values shown for mainnet (`TIER_SCALE=1`).

| Tier byte | Enum name | Public name | Total buy-in (SOL) | Prize (SOL) | Fee (SOL) |
| --------- | --------- | ----------- | ------------------ | ----------- | --------- |
| 0         | Micro     | Copper      | 0.05               | 0.045       | 0.005     |
| 1         | Bronze    | Bronze      | 0.10               | 0.09        | 0.01      |
| 2         | Silver    | Silver      | 0.25               | 0.225       | 0.025     |
| 3         | Gold      | Gold        | 0.50               | 0.45        | 0.05      |
| 4         | Platinum  | Platinum    | 1.00               | 0.90        | 0.10      |
| 5         | Diamond   | Diamond     | 2.00               | 1.80        | 0.20      |
| 6         | Black     | Black       | 5.00               | 4.50        | 0.50      |

The Rust enum names the first variant `Micro`. The public display name is `Copper`.

### SngPool decode facts

The `SngPool` account is 150 bytes including the 8-byte Anchor discriminator. Two fields are read often by integrations. Both offsets count from the start of the account data.

| Field              | Type              | Offset |
| ------------------ | ----------------- | ------ |
| `waiting_count`    | u32 little-endian | 27     |
| `active_match_set` | bool (1 byte)     | 83     |

`waiting_count` is the number of active players queued across all pages. `active_match_set` is true while a match is live. For the remaining field layout, see [../04-architecture/state-accounts.md](/architecture/state-accounts.md) and [reading-accounts.md](/building-on-the-protocol/reading-accounts.md).

## Steps

1. Install `@solana/web3.js` and `@coral-xyz/anchor`.
2. Save the fastpoker IDL JSON where your code can import it. The example imports `../../target/idl/fastpoker.json`. Adjust the relative path to match your project.
3. Create `fastpoker.ts` with the contents below. It exports the program IDs, the IDL, the PDA helpers, and `sendAndConfirm`.
4. Create a `Connection` to your L1 RPC.
5. Load a wallet. Use a `PublicKey` for read-only work or a `Signer` for transactions.
6. Import from `./fastpoker` in every later page.

## The shared module: fastpoker.ts

Copy this file as is. The seed strings and program IDs are verified and must not change.

```ts
// fastpoker.ts: shared FastPoker on-chain helpers (web3.js v1)
import {
  PublicKey,
  Connection,
  Transaction,
  TransactionInstruction,
  Signer,
  Commitment,
  ComputeBudgetProgram,
} from '@solana/web3.js';
import idl from '../../target/idl/fastpoker.json'; // Anchor IDL (address + discriminators)

export const FASTPOKER_IDL = idl;

// Program IDs (verified from constants.rs / IDL address)
export const FASTPOKER_PROGRAM_ID = new PublicKey(
  'PokerXYdXL2SKNnfGbv1WE7vJHipTpNsfZbZeVvoJLn',
);
export const FASTPOKER_REGISTRY_PROGRAM_ID = new PublicKey(
  'pokerQBdo685uLSkpVSyZ1vWooPYYTUhGkeKAHyCmax',
);
export const PERMISSION_PROGRAM_ID = new PublicKey(
  'ACLseoPoyC3cBqoUtkbjZ4aDrkurZW86v19pXz2XQnp1',
);
export const STEEL_PROGRAM_ID = new PublicKey(
  'FASTPjXb68fPW9JRYSBS3EDoaT6inz84GoqkPK52dsA9',
);
export const DELEGATION_PROGRAM_ID = new PublicKey(
  'DELeGGvXpWV2fqJUhqcF5ZSYMS4JTLjteaAMARRSaeSh',
);

// PDA seeds (byte-exact with on-chain seeds = [...] arrays)
const TABLE_SEED = Buffer.from('table');
const SEAT_SEED = Buffer.from('seat');
const SEAT_CARDS_SEED = Buffer.from('seat_cards');
const DECK_STATE_SEED = Buffer.from('deck_state');
const PLAYER_SEED = Buffer.from('player');
const VAULT_SEED = Buffer.from('vault');
const SNG_POOL_SEED = Buffer.from('sng_pool');
const SNG_POOL_VAULT_SEED = Buffer.from('sng_pool_vault');
const SNG_MATCH_SEED = Buffer.from('sng_match');
const SNG_QUEUE_MARKER_SEED = Buffer.from('sng_queue_marker');
const SNG_QUEUE_PAGE_SEED = Buffer.from('sng_queue_page');
const SNG_DUEL_SEED = Buffer.from('sng_duel');
const SNG_SETTLEMENT_SEED = Buffer.from('sng_settlement');
const EMISSION_CTRL_SEED = Buffer.from('emission_ctrl');
const POOL_IDLE_SEED = Buffer.from('pool_idle');

const u16le = (n: number) => { const b = Buffer.alloc(2); b.writeUInt16LE(n); return b; };
const u64le = (n: bigint) => { const b = Buffer.alloc(8); b.writeBigUInt64LE(n); return b; };

// PDA-derivation helpers (program = FASTPOKER_PROGRAM_ID)
export const getTablePda = (tableId: Uint8Array) =>
  PublicKey.findProgramAddressSync([TABLE_SEED, Buffer.from(tableId)], FASTPOKER_PROGRAM_ID);

export const getSeatPda = (table: PublicKey, seatIndex: number) =>
  PublicKey.findProgramAddressSync(
    [SEAT_SEED, table.toBuffer(), Buffer.from([seatIndex])], FASTPOKER_PROGRAM_ID);

export const getSeatCardsPda = (table: PublicKey, seatIndex: number) =>
  PublicKey.findProgramAddressSync(
    [SEAT_CARDS_SEED, table.toBuffer(), Buffer.from([seatIndex])], FASTPOKER_PROGRAM_ID);

export const getDeckStatePda = (table: PublicKey) =>
  PublicKey.findProgramAddressSync([DECK_STATE_SEED, table.toBuffer()], FASTPOKER_PROGRAM_ID);

export const getPlayerPda = (wallet: PublicKey) =>
  PublicKey.findProgramAddressSync([PLAYER_SEED, wallet.toBuffer()], FASTPOKER_PROGRAM_ID);

export const getTableVaultPda = (table: PublicKey) =>
  PublicKey.findProgramAddressSync([VAULT_SEED, table.toBuffer()], FASTPOKER_PROGRAM_ID);

// SNG pools/vaults are keyed by (gameType: u8, tier: u8)
export const getSngPoolPda = (gameType: number, tier: number) =>
  PublicKey.findProgramAddressSync(
    [SNG_POOL_SEED, Buffer.from([gameType]), Buffer.from([tier])], FASTPOKER_PROGRAM_ID);

export const getSngPoolVaultPda = (gameType: number, tier: number) =>
  PublicKey.findProgramAddressSync(
    [SNG_POOL_VAULT_SEED, Buffer.from([gameType]), Buffer.from([tier])], FASTPOKER_PROGRAM_ID);

export const getSngMatchPda = (sngPool: PublicKey, matchId: bigint) =>
  PublicKey.findProgramAddressSync(
    [SNG_MATCH_SEED, sngPool.toBuffer(), u64le(matchId)], FASTPOKER_PROGRAM_ID);

export const getSngQueueMarkerPda = (sngPool: PublicKey, player: PublicKey) =>
  PublicKey.findProgramAddressSync(
    [SNG_QUEUE_MARKER_SEED, sngPool.toBuffer(), player.toBuffer()], FASTPOKER_PROGRAM_ID);

export const getSngQueuePagePda = (sngPool: PublicKey, pageIndex: number) =>
  PublicKey.findProgramAddressSync(
    [SNG_QUEUE_PAGE_SEED, sngPool.toBuffer(), u16le(pageIndex)], FASTPOKER_PROGRAM_ID);

export const getSngDuelStatePda = (table: PublicKey) =>
  PublicKey.findProgramAddressSync(
    [SNG_DUEL_SEED, table.toBuffer()], FASTPOKER_PROGRAM_ID);

export const getSngSettlementRecordPda = (table: PublicKey, startHand: bigint) =>
  PublicKey.findProgramAddressSync(
    [SNG_SETTLEMENT_SEED, table.toBuffer(), u64le(startHand)], FASTPOKER_PROGRAM_ID);

export const getEmissionCtrlPda = () =>
  PublicKey.findProgramAddressSync([EMISSION_CTRL_SEED], FASTPOKER_PROGRAM_ID);

export const getPoolIdlePda = (gameType: number, tier: number) =>
  PublicKey.findProgramAddressSync(
    [POOL_IDLE_SEED, Buffer.from([gameType]), Buffer.from([tier])], FASTPOKER_PROGRAM_ID);

// sendAndConfirm helper
export async function sendAndConfirm(
  connection: Connection,
  ixs: TransactionInstruction[],
  signers: Signer[],
  opts: { commitment?: Commitment; computeUnits?: number } = {},
): Promise<string> {
  const { commitment = 'confirmed', computeUnits } = opts;
  const tx = new Transaction();
  if (computeUnits) tx.add(ComputeBudgetProgram.setComputeUnitLimit({ units: computeUnits }));
  tx.add(...ixs);
  const { blockhash, lastValidBlockHeight } =
    await connection.getLatestBlockhash(commitment);
  tx.recentBlockhash = blockhash;
  tx.feePayer = signers[0].publicKey;
  tx.sign(...signers);
  const sig = await connection.sendRawTransaction(tx.serialize());
  await connection.confirmTransaction(
    { signature: sig, blockhash, lastValidBlockHeight }, commitment);
  return sig;
}
```

Notes on the helper:

* `getSngQueuePagePda` encodes `page_index` as a `u16` little-endian. `getSngMatchPda` encodes `match_id` and `getSngSettlementRecordPda` encodes `start_hand` as `u64` little-endian. Game type, tier, and seat indexes are single bytes.
* `sendAndConfirm` uses `signers[0]` as the fee payer. Pass the payer first.
* The IDL carries the `address` and the per-instruction `discriminator` arrays. Anchor instruction names in the IDL are snake\_case.

## Example

This runnable example imports the shared module from `./setup.md` (the `fastpoker.ts` defined above), opens a `Connection`, loads a wallet, builds an Anchor `Program` from the IDL, and derives two PDAs. It does not send a transaction.

```ts
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
import { AnchorProvider, Program, Wallet } from '@coral-xyz/anchor';
import {
  FASTPOKER_IDL,
  FASTPOKER_PROGRAM_ID,
  getPlayerPda,
  getSngPoolPda,
} from './fastpoker'; // the module from this page

async function main() {
  // 1. Connection to an L1 RPC.
  const connection = new Connection('https://api.mainnet-beta.solana.com', 'confirmed');

  // 2. Load a wallet. Replace with your own keypair or wallet adapter.
  const keypair = Keypair.generate();
  const wallet = new Wallet(keypair);

  // 3. Build an Anchor Program from the IDL (address comes from the IDL).
  const provider = new AnchorProvider(connection, wallet, { commitment: 'confirmed' });
  const program = new Program(FASTPOKER_IDL as any, provider);
  console.log('program id', program.programId.toBase58());
  console.log('matches', program.programId.equals(FASTPOKER_PROGRAM_ID));

  // 4. Derive the Player PDA for this wallet.
  const [playerPda] = getPlayerPda(wallet.publicKey);
  console.log('player pda', playerPda.toBase58());

  // 5. Derive the SNG pool PDA for the Copper heads-up tier.
  //    gameType 0 = SitAndGoHeadsUp, tier 0 = Copper.
  const [poolPda] = getSngPoolPda(0, 0);
  console.log('copper HU pool', poolPda.toBase58());
}

main().catch(console.error);
```

## Result

After this page you have:

* The two libraries installed.
* An open `Connection` to your L1 RPC.
* A loaded wallet, as a `PublicKey` or a `Signer`.
* A `fastpoker.ts` module that exports the program IDs, the IDL, the PDA helpers, and `sendAndConfirm`.

Every later page imports from this module. PDAs derive deterministically, so the same inputs always produce the same addresses.

## Pitfalls

* Wrong IDL path. The example imports `../../target/idl/fastpoker.json`. Set the relative path to where you saved the IDL, or the build fails.
* Editing a seed string. The seed byte strings must match the on-chain `seeds = [...]` arrays exactly. A changed string derives a different address and the instruction fails.
* Wrong integer width. `page_index` is `u16` little-endian and `match_id` is `u64` little-endian. The other index arguments are single bytes. Do not swap widths.
* Out-of-range single-byte index. `seat_index`, `game_type`, and `tier` are single bytes and must be under 256.
* Using `feePayer` other than the first signer. `sendAndConfirm` sets `feePayer` to `signers[0].publicKey`. Order the array so the payer is first.
* Read-only loads do not need a `Signer`. Deriving PDAs and fetching accounts work with a `PublicKey` alone. Reserve `Signer` for transactions.
* Confusing `Micro` and `Copper`. The Rust enum variant is `Micro`. The public name is `Copper`. The tier byte for both is 0.

## See also

* [quickstart.md](/building-on-the-protocol/quickstart.md)
* [deriving-pools.md](/building-on-the-protocol/deriving-pools.md)
* [reading-accounts.md](/building-on-the-protocol/reading-accounts.md)
* [../04-architecture/state-accounts.md](/architecture/state-accounts.md)
