> ## Documentation Index
> Fetch the complete documentation index at: https://getequity.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Solana — Programs & Addresses

> The program IDs, PDAs, and mint addresses you need to trade RWA tokens on Solana.

On Solana you trade against the **`rwa_market`** program. Per-asset state lives in
**PDAs keyed by the SPL mint** — the mint address is effectively the "token id", and you
derive every account you need from it. Tokens are **Token-2022** mints.

## Program IDs (same on every cluster)

A program id equals its deploy keypair's pubkey, so **the same id is used on
devnet/testnet/mainnet**.

| Program | Program ID | What you use it for |
| - | - | - |
| `rwa_market` | `Fv7op93iVyTGKVeD3bQvh3dsi6u5g8ULyvvg48dRqWqe` | `buy` / `sell`, read the price |
| `rwa_token` | `8eiSiyHwkB3s5x4jNEWrfnPFyWULYMRApFe8ungsu9Gy` | read asset terms & payout token |
| `rwa_exit` | `7q7cns2MDf2sNVtwGHczub2oTrz8YjqFutEHHJZuQKqc` | `claim` at redemption |
| `rwa_vault` | `A5SYMN5DGKWo8oPwDgWUxCZHZA7TaCkLbguSrLiWc91` | derive the vault token accounts a trade touches |
| `rwa_payout` | `GYr9jEoke8uAFKyP4zfv7vCh38Ur3zyvjmAjc2nEZ1jM` | `claim_payout` for a coupon you are owed |
| `rwa_automation` | `Af4LiYSyEbHj8XBdbjRzFzpx31WmqS3LNXHyHbc8hxzg` | payout schedule per asset |
| `rwa_venue` | `BNySR58kNhV646LGVERkyxD4DMk7puamJ1DDpbXMHhpW` | [market-making pools](https://getequity.io/docs/onchain/market-making.md) — **devnet only**, not deployed on mainnet |

> [!WARNING]
>
> **RWA mints are Token-2022, at 6 decimals.** A classic SPL-Token client will fail on
> them: use `TOKEN_2022_PROGRAM_ID` and `transferChecked`. The tradeable mints have no
> active transfer hook, so a transfer needs no extra accounts.
>
> Mainnet is live. The programs' upgrade authority is a 2-of-3 Squads v4 multisig
> (`HnvyE4qeeQ81hWniSZzBWgivg1q1ZvLnZx1g2t3fTidV`), so no single key can replace program
> logic.

## Per-cluster addresses

Only the RPC endpoint and the payment-token mints differ per cluster.

| | Devnet | Mainnet-beta |
| - | - | - |
| RPC (default) | `https://api.devnet.solana.com` | `https://api.mainnet-beta.solana.com` |
| USDC mint (SPL Token, 6 dp) | `4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU` | `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v` |
| CNGN mint (Token-2022, 6 dp) | `HfJWS8vJHvxKn5xW3uLXkTmEy4jny3G45QnS1Eab5sg` | `3jiqwBQVRC5zRwHyqvnkQurebJ5RNxg3F5fXMwaxgkv8` |

> [!NOTE]
>
> Which payment mint applies is per-asset — read `payout_token` from the token's
> `TokenMeta` before trading.

### Getting cNGN on Solana

Most assets are naira-denominated and settle in cNGN, so you need cNGN on Solana before you
can buy. Third-party services, listed for convenience — GetEquity does not operate them.

| Venue | What |
| - | - |
| [Orca — cNGN/USDC](https://www.orca.so/pools/DdYMJgiucDr2VuTrYceYDDgGsUAtihvAkUdPjFjYm6ZS) | swap USDC for cNGN |
| [GMGN — cNGN](https://gmgn.ai/sol/token/3jiqwBQVRC5zRwHyqvnkQurebJ5RNxg3F5fXMwaxgkv8) | price and liquidity charts |

The Orca pool is a Whirlpool at `DdYMJgiucDr2VuTrYceYDDgGsUAtihvAkUdPjFjYm6ZS`, holding
cNGN as token A and USDC as token B.

Fiat on-ramps sell cNGN too, but they settle it on whichever chain that service uses — not
necessarily Solana. The issuer keeps a fuller list at [cngn.co/merchants](https://cngn.co/merchants).

> [!WARNING]
>
> **Check the mint before you swap.** "cNGN" is a name, not an identity — anyone can create a
> token using it, and a swap interface will route you into one on a symbol match. On Solana,
> cNGN is `3jiqwBQVRC5zRwHyqvnkQurebJ5RNxg3F5fXMwaxgkv8` and nothing else. Paste the mint
> rather than picking by name or logo.
>
> It is **Token-2022**, not classic SPL — a wallet or client that only speaks SPL Token will
> not see it. And cNGN on Solana is a different token from cNGN on Base
> (`0x46C85152bFe9f96829aA94755D9f915F9B10EF5F`): they are not bridgeable by sending, and a
> transfer to an address on the wrong chain is lost.

## Tradeable RWA tokens (mainnet)

Token-2022 mints, 6 decimals. Read on chain 2026-10-14 — re-check
rather than trusting this table, and note that **a mint existing does not mean it trades**.

| Symbol | Asset | Mint | Tradeable |
| - | - | - | - |
| DPRI | Dangote Petroleum Refinery IPO | `4SARoiczTriUakwqmNtmP45hDrFV46umaJVwjQC8JLiK` | yes |
| gNTB | GetEquity Nigerian Treasury Bills Fund | `CsMkseiQZJiaXSyiE7NZayosWWQmycwdYxU85aYCxg5d` | yes |
| NTBS5 | Nigerian Treasury Bill Series 5 | `9C3xMC6J3dWnwLof5xwCasN4tVtNcxVCpbCbrVBmGWwj` | **no** — `is_active: false` |

> [!NOTE]
>
> `is_active` lives on the `Asset` PDA (`["asset", mint]` under `rwa_market`), not on the mint
> or the token metadata. An inactive asset still has a mint, still has holders and still shows a
> price, so the only reliable check is that field — buys and sells revert without it.

> [!NOTE]
>
> These are **mainnet** mints and do not exist on devnet. Point your connection at the
> cluster that matches the mints you are using — a mainnet mint queried on devnet simply
> reads as a missing account, which is easy to mistake for an empty balance.

## Tradeable RWA tokens (devnet)

Only assets with a Solana mint can be traded here. Assets listed as EVM-only have no mint
on this cluster.

| Symbol | Asset | SPL mint |
| - | - | - |
| DPRI | Dangote Petroleum Refinery IPO | `Fvdk1zV7WqfDBxg5S4qcyaF7TCuABAso6KcRR8Nnvdya` |
| gNTB | GetEquity Nigerian Treasury Bills Fund (devnet copy) | `55dDdsg9WN4s1WVX8u2KGDLyusWW4MchaLgtEgX96TET` |
| CHDNRE | Chapel Hill Denham NREIT | *EVM-only — no Solana mint* |
| ARMNGF | ARM NGN Mutual Fund | *EVM-only — no Solana mint* |

## Market-making venue (devnet)

Each asset's venue is a PDA of the `rwa_venue` program: `["venue", mint]`. The account
exists only for assets that have a venue. See [market-making pools](https://getequity.io/docs/onchain/market-making.md)
and [become a market maker](https://getequity.io/docs/onchain/become-a-market-maker.md).

| Asset | Mint | Venue |
| - | - | - |
| gNTB | `55dDdsg9WN4s1WVX8u2KGDLyusWW4MchaLgtEgX96TET` | `4ZvJrgar8dVJY8bQy451pS9FcJFAzbVF38Ksn55ipuYj` |

## Credit (Kamino Lend, devnet)

The gNTB / cNGN lending market. See [credit on Solana](https://getequity.io/docs/onchain/credit-solana.md).

| Item | Address |
| - | - |
| klend program | `KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD` |
| Lending market | `HMovGgLerJ4XZa1xzRLStHSEogXo77fWRtQN8AGdWW2D` |
| gNTB reserve (collateral) | `5czpRfDs69uMbcytnGSrg1NLxhUMaZrzn4Nx72vsvuUz` |
| cNGN reserve (lend / borrow) | `6DDM2QqTcm5uuKtShUx5usGnH42QThos1o6v7hFJ3hCQ` |
| Scope prices account | `Bt77wjkiDjwwwPZhuCX7aFRBocwgihuu336eBV6EVVrJ` |
| Scope program (Kamino devnet) | `3Vw8Ngkh1MVJTPHthmUbmU2XKtFEkjYvJzMqrv2rh9yX` |

Read on chain 2026-10-14. There is no mainnet market yet.

## Deriving the accounts you need

Every per-asset account is a PDA of a constant seed + the SPL mint pubkey.

| PDA | Program | Seeds | Why you need it |
| - | - | - | - |
| `Asset` (listing) | `rwa_market` | `["asset", mint]` | holds price/fee; passed to `buy`/`sell` |
| `TokenMeta` | `rwa_token` | `["meta", mint]` | read `payout_token`, decimals, terms |
| `Vault` | `rwa_vault` | `["vault", mint]` | owns the vault token accounts a trade moves |
| `ExitState` | `rwa_exit` | `["exit", mint]` | passed to `claim` at redemption |
| `Schedule` | `rwa_automation` | `["sched", mint]` | the asset's payout schedule |
| `PayoutRound` | `rwa_payout` | `["payout", mint, round_idx_u64_le]` | a single distribution round |
| `Claimed` | `rwa_payout` | `["claimed", round, holder]` | marks a holder as paid for that round |

`round_idx` is a little-endian `u64`, not a string — `new BN(idx).toArrayLike(Buffer, "le", 8)`.

### The program-level `Config`

One per program, not per asset, and seeded only by a constant:

| PDA | Seeds | Derived against |
| - | - | - |
| `Config` | `["config"]` | each program's own id |

It holds that program's `authority` (the Squads vault) and `operator`. You do not pass it to
`buy`/`sell`, but every account-creating instruction takes it, and it is what makes per-asset
admin fields non-forgeable: they are written from `Config.authority` rather than from whoever
calls first. If you build an instruction from an older IDL that has no `config` account, the
program rejects it.

```js theme={null}
// @solana/web3.js
const [assetPda] = PublicKey.findProgramAddressSync([Buffer.from("asset"), mint.toBuffer()], RWA_MARKET_PROGRAM_ID);
const [metaPda]  = PublicKey.findProgramAddressSync([Buffer.from("meta"),  mint.toBuffer()], RWA_TOKEN_PROGRAM_ID);
const [vaultPda] = PublicKey.findProgramAddressSync([Buffer.from("vault"), mint.toBuffer()], RWA_VAULT_PROGRAM_ID);
const [exitPda]  = PublicKey.findProgramAddressSync([Buffer.from("exit"),  mint.toBuffer()], RWA_EXIT_PROGRAM_ID);

// Program-level config — one per program, no mint in the seeds.
const [marketConfig] = PublicKey.findProgramAddressSync([Buffer.from("config")], RWA_MARKET_PROGRAM_ID);
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.