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

# Solana

> Where the API differs when a quote settles on Solana.

Solana chains are entries in [`GET /v1/config`](/integrators/api/config) with `"kind": "solana"`. Chain ids
are 101 (mainnet-beta), 102 (testnet), 103 (devnet), and 104 and above for local validators. Keys and
signatures are base58; transactions are standard base64; amounts are base-unit decimal strings.

## Who signs what

A settlement transaction has three signers: the **maker** (the resolver node), the **taker**, and the **fee
payer** (the relay's keeper). The taker needs no SOL.

1. The node signs as maker when it answers the RFQ.
2. The relay checks the transaction byte for byte against the terms it asked for, simulates it, and quotes it.
3. The taker's client rebuilds the same transaction from `/v1/config` and the quote, checks it, and signs as taker.
4. The keeper signs last as fee payer and submits.

The taker repays the keeper in the pair's fee mint through the gas-fee instruction. That fee includes the
rent for the taker's receiving token account when it does not exist yet (`gasFee.rent` is true). The
canonical transaction pays only the taker, so `recipient` must equal `taker`.

## Fee mints and pairs

`fee.tokens` in the chain's config lists the mints fees may be charged in, in priority order. A pair's fee
mint is the first of `fee.tokens` that is its sell mint or its buy mint: cNGN/USDC is charged in USDC and
cNGN/USDT in USDT. `pairs` lists the corridors the relay quotes on the chain, as mint pairs. A pair outside
that list is refused `400 unsupported_pair`.

## Requesting a quote

Same as EVM, with base58 values and a base58 taker proof:

```json theme={null}
{ "chainId": 103, "sellToken": "<mint>", "buyToken": "<mint>", "sellAmount": "10000000000", "taker": "<pubkey>",
  "takerProof": { "issuedAt": 1757750400, "signature": "<base58>" } }
```

The response has `"kind": "solana"` and a `solana` block instead of `permit`, `auth`, `offers` and `typedData`:

```json theme={null}
"solana": {
  "transaction": "<base64, maker-signed>", "messageHash": "0x<sha256 of the message>",
  "maker": "<pubkey>", "feePayer": "<keeper>", "treasury": "<pubkey>", "program": "<program>",
  "recentBlockhash": "<base58>", "expireAt": "1757750430",
  "feeMint": "<mint>", "feeBps": 5, "gasFee": "<same as gasFee.amount>",
  "computeUnitLimit": 200000, "computeUnitPrice": "10000"
}
```

**Amounts.** Let `fee` be `floor(leg × bps / 10000)`, where the leg is `sellAmount` when the fee mint is the
sell mint and `received` otherwise. Let `gas` be `gasFee.amount`.

| Fee mint      | `takerPays`              | `proceeds`                                                               |
| ------------- | ------------------------ | ------------------------------------------------------------------------ |
| The sell mint | `sellAmount + fee + gas` | `received`                                                               |
| The buy mint  | `sellAmount`             | `received − fee − gas` (the offer is rejected if this is not above zero) |

## Checks before the wallet signs

<Steps>
  <Step title="Build the terms">
    Take the fee mint as the first of `config.fee.tokens` that is `sellToken` or `buyToken`. Check that
    `solana.feeMint`, `fee.token` and `gasFee.token` are that mint. Then:

    ```
    { program: config.program, maker: solana.maker, taker, feePayer: config.keeper, treasury: config.treasury,
      sell: config token for sellToken, buy: config token for buyToken, feeMint, feeBps: config.fee.bps,
      gasFee: gasFee.amount, inputAmount: sellAmount, outputAmount: received, expireAt: solana.expireAt,
      computeUnitLimit: config.computeUnitLimit, computeUnitPrice: config.computeUnitPrice }
    ```
  </Step>

  <Step title="Verify the transaction">
    `verifySettlement(terms, base64Decode(solana.transaction), [solana.maker])` must pass. The reference
    implementation is `panofx-api/src/domain/solana/settlement.ts`, copied byte for byte into the web app.
  </Step>

  <Step title="Check the amounts">
    What the wallet will show must match `takerPays`, `proceeds`, `fee` and `gasFee` computed from those terms.
  </Step>

  <Step title="After signing">
    Check that the message bytes are unchanged, the taker's signature verifies, and the maker's signature is
    still present.
  </Step>
</Steps>

## Submitting

`POST /v1/executions` with `{ "quoteId": "<uuid>", "transaction": "<base64>" }`: the quote's transaction
with the taker's signature added. The fee payer's slot may be empty. The relay requires the message bytes
to equal the quoted message, verifies both signatures, and builds the transaction it sends from its own
stored copy plus the taker's signature and the keeper's.

## Other routes

* `GET /v1/executions/:id`: `txHash` is the base58 transaction signature; `gasUsed` is compute units consumed.
* `GET /v1/quotes/:id`: `txHash` values are base58 and `settlement.block` is the slot. `final` is true once the transaction is finalized.
* `GET /v1/limits?chainId=103&…`: the same shape as EVM, with `allowance` equal to `balance`.
* `GET /v1/corridors`: a Solana chain lists only its `pairs`, each with its own fee mint.
* `GET /v1/health`: the chain's entry has `slot`, `keeperLamports` and `keeperLow`.
* Limit-order routes answer `400 limit_orders_unsupported`.

Out of scope on Solana: limit orders, a recipient other than the taker, and Permit2 or allowance steps.
