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

# Solana Program

> Meridian x402 payment facilitator on Solana (SVM)

## Overview

`meridian_x402` is the Solana (SVM) port of the Meridian facilitator. It is the
equivalent of [`X402ProxyFacilitator.transferWithAuthorization`](/payments/smart-contracts)
on EVM: it pulls USDC from a payer, subtracts platform and treasury fees, and
settles the remainder to the recipient — atomically, in a single transaction.

This first version is intentionally minimal. It settles same-chain USDC
payments on Solana and nothing else: no cross-chain (Across) routing, no MRDN
cashback, and no batched settlement (those live only on the EVM facilitator
today).

<Note>
  Integrations should read the program ID, facilitator pubkey, and on-chain
  config from `GET /v1/solana/facilitator` at runtime rather than hardcoding
  them, so they keep working across any redeploys.
</Note>

## Deployment

| Network         | Cluster      | Program ID                                    | Status |
| --------------- | ------------ | --------------------------------------------- | ------ |
| `solana`        | mainnet-beta | `Ro6hz1smrm5zDh73849eDqKna9dE1EkPsWekAB5rBWm` | Live   |
| `solana-devnet` | devnet       | `Ro6hz1smrm5zDh73849eDqKna9dE1EkPsWekAB5rBWm` | Live   |

Built with **Anchor 0.32.1**. The config account is a PDA at seeds `["config"]`,
derived from the program ID.

USDC mints the program settles against:

| Network         | USDC Mint                                      |
| --------------- | ---------------------------------------------- |
| `solana`        | `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v` |
| `solana-devnet` | `4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU` |

## Why it looks different from the EVM facilitator

On EVM, the payer cannot join the settlement transaction without paying gas, so
x402 relies on EIP-3009: the payer signs an off-chain authorization, and the
facilitator submits it on-chain where the token contract verifies the signature.

Solana needs none of that machinery:

1. **A transaction can have multiple signers, and any of them can pay the fee.**
   The payer (`from`) signs the settlement transaction as the authority of their
   USDC token account. The backend signer co-signs as `payer` (fee payer) and
   submits it. The payer's transaction signature *is* the authorization — no
   in-program signature verification required.
2. **Settlement is atomic within one transaction**, so there is no escrow hop.
   The EVM contract first pulls the full amount into itself and then distributes;
   here the program splits funds directly from the payer's token account via up
   to three `transfer_checked` CPIs (platform fee, treasury fee, recipient net).

From the client's perspective the x402 flow is unchanged:

```mermaid theme={"system"}
sequenceDiagram
    participant User as User (payer)
    participant Seller
    participant Facilitator as Meridian Facilitator
    participant Program as meridian_x402

    Seller->>User: HTTP 402 + payment requirements
    User->>Facilitator: GET /v1/solana/facilitator<br/>(fee payer, program id, mint, treasury, fees, paused)
    User->>User: Build transfer_with_authorization tx, sign as token authority
    User->>Seller: X-PAYMENT: base64(paymentPayload { transaction })
    Seller->>Facilitator: POST /v1/settle (paymentPayload + requirements)
    Facilitator->>Facilitator: Validate the signed transaction
    Facilitator->>Program: Co-sign as fee payer, submit
    Program->>Program: Enforce window, split fees, settle net atomically
    Program-->>Facilitator: InstantSettlement event
    Facilitator-->>Seller: { success, transaction: signature }
```

What is kept for parity with the EVM `transferWithAuthorization`:

* `valid_after` / `valid_before` settlement window, checked against the on-chain clock
* a 32-byte `nonce` for x402 payment correlation and idempotency
* identical fee economics (platform fee off gross, treasury fee off the remainder)

## Architecture

### Config account

A single global PDA (`seeds = ["config"]`) holds the program's configuration:

| Field              | Type     | Purpose                                               |
| ------------------ | -------- | ----------------------------------------------------- |
| `authority`        | `Pubkey` | Admin: fee updates, pause, treasury / signer rotation |
| `backend_signer`   | `Pubkey` | Backend signer allowed to submit settlements          |
| `treasury`         | `Pubkey` | Wallet that owns the treasury-fee token account       |
| `usdc_mint`        | `Pubkey` | The only mint the program settles                     |
| `treasury_fee_bps` | `u16`    | Protocol fee in basis points (max `1000` = 10%)       |
| `paused`           | `bool`   | Circuit breaker for the settlement entrypoint         |
| `bump`             | `u8`     | Config PDA bump                                       |

### Instructions

| Instruction                                                                              | Access                      | Purpose                    |
| ---------------------------------------------------------------------------------------- | --------------------------- | -------------------------- |
| `initialize(backend_signer, treasury, treasury_fee_bps)`                                 | one-time                    | Create the `Config` PDA    |
| `transfer_with_authorization(value, valid_after, valid_before, nonce, platform_fee_bps)` | payer + backend signer sign | Settle a payment           |
| `set_treasury_fee_bps(bps)`                                                              | authority                   | Update the protocol fee    |
| `set_treasury(pubkey)`                                                                   | authority                   | Rotate the treasury wallet |
| `set_backend_signer(pubkey)`                                                             | authority                   | Rotate the backend signer  |
| `set_paused(bool)`                                                                       | authority                   | Pause / unpause settlement |

`transfer_with_authorization` account list:

| Account           | Signer | Notes                                                      |
| ----------------- | ------ | ---------------------------------------------------------- |
| `payer`           | ✓      | Backend signer (facilitator): pays the tx fee              |
| `from`            | ✓      | The paying user (USDC token authority)                     |
| `config`          |        | Config PDA                                                 |
| `usdc_mint`       |        | Must match `config.usdc_mint`                              |
| `from_token`      |        | Payer's USDC associated token account (authority = `from`) |
| `recipient_token` |        | Recipient's USDC associated token account                  |
| `platform_token`  |        | *Optional* — omit for payments without a platform fee      |
| `treasury_token`  |        | USDC token account owned by `config.treasury`              |
| `token_program`   |        | SPL Token / Token-2022 program                             |

<Warning>
  The settlement transaction does **not** create any token accounts. The payer,
  recipient, platform (if used), and treasury USDC associated token accounts
  must already exist, or settlement fails. The backend signer pays only the
  transaction fee, never rent.
</Warning>

### Fee math

Identical to the EVM facilitator's `_settleAndDistribute`:

```
platform_fee = value * platform_fee_bps / 10_000       (0 if no platform account)
remainder    = value - platform_fee
treasury_fee = remainder * treasury_fee_bps / 10_000   (skipped if platform == treasury)
net_amount   = remainder - treasury_fee                (→ recipient)
```

Both fees are capped at `MAX_FEE_BPS = 1000` (10%). Multiplication is widened to
`u128` so it cannot overflow. Each settlement emits an `InstantSettlement`
event with the full breakdown:

```rust theme={"system"}
InstantSettlement {
    from,            // payer wallet
    recipient,       // recipient token account
    platform,        // Option<platform token account>
    nonce,           // [u8; 32] — x402 payment correlation id
    gross_amount,    // value
    platform_fee,
    treasury_fee,
    net_amount,      // credited to the recipient
}
```

### Replay protection & the nonce

Replay protection is **transaction-level**, matching the reference x402 SVM
scheme. The paying user signs the settlement transaction itself, which embeds a
recent blockhash: the runtime rejects duplicate signatures, and the transaction
expires with the blockhash. Settling the same payment again would require a
fresh signature from the payer.

No nonce account is created on-chain, so no rent is locked per payment. The
32-byte `nonce` argument is carried into the `InstantSettlement` event for x402
payment correlation, and the facilitator additionally refuses to co-sign a nonce
it has already settled (a settlement cache in the payments table).

### Errors

| Error                      | Meaning                                              |
| -------------------------- | ---------------------------------------------------- |
| `Paused`                   | The settlement entrypoint is paused                  |
| `Unauthorized`             | Caller is not the config authority / backend signer  |
| `FeeTooHigh`               | A fee exceeds `MAX_FEE_BPS` (10%)                    |
| `InvalidValue`             | `value` (or net after fees) is not greater than zero |
| `AuthorizationNotYetValid` | `now <= valid_after`                                 |
| `AuthorizationExpired`     | `now >= valid_before`                                |
| `InvalidMint`              | Mint does not match `config.usdc_mint`               |

(Treasury-token ownership is enforced by an Anchor account constraint, which
surfaces as a generic constraint error rather than a program-specific code.)

## How settlement is validated

The client builds and payer-signs the settlement transaction; the facilitator's
`POST /v1/settle` acts as the co-signing safety boundary. Before it adds the
backend signer's signature and submits, it checks that the transaction does
exactly one thing the backend signer is willing to pay for:

* the fee payer is the backend signer, and the transaction contains **exactly one**
  `transfer_with_authorization` instruction against the Meridian program
* `payer` (account 0) is the backend signer; `from` (account 1) is a different signer
* `config` is the expected Config PDA and the token program is the SPL Token program
* the mint equals `paymentRequirements.asset`, and `value` equals `maxAmountRequired`
* the validity window is still open (`valid_after <= now`, `valid_before >= now + 6s`)
* `from_token`, `recipient_token`, and any `platform_token` are the correct
  associated token accounts for the resolved recipient / platform, and
  `platform_fee_bps` matches the organization's configured fee
* the payer's wallet signature is present and valid, and the backend signer has
  not already signed

Because the program only moves USDC with the payer's signature and never creates
accounts, a structurally valid transaction means the backend signer risks only
the transaction fee. The recipient (and platform, in platform mode) is resolved
from the caller's Meridian **organization settings**, not blindly trusted from
the request.

## Deliberately omitted (vs. EVM V6)

Settlement is instant to every party, so there are no fee-accrual balances or
withdraw functions. Also out of scope for this version, all of which remain
EVM-only for now:

* MRDN cashback
* Cross-chain (Across) settlement
* Batch payments

## Building & running locally

The program lives in [`contracts-sol/`](https://github.com/meridian-protocol/meridian/tree/main/contracts-sol) in the Meridian
monorepo. Requires the Solana CLI (Agave) and Anchor 0.32.1 (`avm use 0.32.1`).

```bash theme={"system"}
cd contracts-sol
anchor build   # compile + generate the IDL (target/idl/meridian_x402.json)
anchor test    # spin up a local validator and run the test suite
```

The test suite covers the exact fee splits (with and without a platform fee),
nonce replay rejection, the validity window, the fee cap, a missing payer
signature, the treasury-as-platform skip, pause / unpause, and admin access
control. See `contracts-sol/README.md` and `contracts-sol/SETUP.md` for
deploying to devnet and running the facilitator + pay app against it.

## References

* [Solana payments (AI skill)](/ai-skills/solana) — end-to-end integration guide
* [Smart Contracts](/payments/smart-contracts) — the EVM facilitator
* [Supported Networks](/api-reference/supported-networks)
* [Payment Types](/api-reference/payment-types/overview)
* [Settle x402 Payment](/api-reference/endpoint/settle-payment)
