# `X402.Solana`
[🔗](https://github.com/cardotrejos/x402/blob/v0.9.0/lib/x402/solana.ex#L1)

Solana address primitives: program IDs, PDA and ATA derivation.

Implements the address arithmetic the `exact` scheme on `solana:*`
networks needs without any RPC or native dependencies:

* Base58 address validation/decoding (32-byte Ed25519 public keys)
* The Ed25519 on-curve check used to reject PDA candidates
  (RFC 8032 point decompression over integer arithmetic)
* `create_program_address/2` / `find_program_address/2` — SHA-256 program
  derived addresses, as specified by the Solana runtime
* `associated_token_address/3` — the Associated Token Account PDA for an
  (owner, mint, token program) triple, which is where the `exact` scheme
  says the payment must land (scheme spec §1.1)

All hashing uses OTP's `:crypto`; no new dependencies.

# `address`

```elixir
@type address() :: String.t()
```

A Base58-encoded Solana address.

# `pubkey`

```elixir
@type pubkey() :: &lt;&lt;_::256&gt;&gt;
```

A raw 32-byte Ed25519 public key or PDA.

# `associated_token_address`
*since 0.6.0* 

```elixir
@spec associated_token_address(address(), address(), address()) ::
  {:ok, address()} | {:error, :invalid_address | :not_found}
```

Derives the Associated Token Account address for an owner and mint.

This is the destination the `exact` SVM scheme requires: the ATA derived
from `payTo` and `asset` under the relevant token program (scheme spec
§1.1–1.2). Seeds are `[owner, token_program, mint]` under the ATA
program.

## Examples

    iex> X402.Solana.associated_token_address(
    ...>   "GyGKxMyg1p9SsHfm15MkNUu1u9TN2JtTspcdmrtGUdse",
    ...>   "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
    ...>   X402.Solana.token_program()
    ...> )
    {:ok, "DNDTCnZkNk358qDFZd9unHtnrc73SsXcpVWtwJJMrR4B"}

    iex> X402.Solana.associated_token_address("bogus", "also-bogus", "nope")
    {:error, :invalid_address}

# `ata_program`
*since 0.6.0* 

```elixir
@spec ata_program() :: address()
```

The Associated Token Account program address.

## Examples

    iex> X402.Solana.ata_program()
    "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"

# `compute_budget_program`
*since 0.6.0* 

```elixir
@spec compute_budget_program() :: address()
```

The Compute Budget program address.

## Examples

    iex> X402.Solana.compute_budget_program()
    "ComputeBudget111111111111111111111111111111"

# `create_program_address`
*since 0.6.0* 

```elixir
@spec create_program_address([binary()], address()) ::
  {:ok, pubkey()} | {:error, :on_curve | :invalid_seeds | :invalid_address}
```

Derives a program address from seeds and a program ID (no bump search).

Seeds are binaries of at most 32 bytes each, at most 16 seeds. Returns
`{:error, :on_curve}` when the SHA-256 candidate lands on the Ed25519
curve (callers should try another bump seed) and
`{:error, :invalid_seeds}` for out-of-range seeds.

# `decode_address`
*since 0.6.0* 

```elixir
@spec decode_address(term()) :: {:ok, pubkey()} | {:error, :invalid_address}
```

Decodes a Base58 address into its raw 32-byte public key.

## Examples

    iex> X402.Solana.decode_address("11111111111111111111111111111111")
    {:ok, <<0::256>>}

    iex> X402.Solana.decode_address("tooshort")
    {:error, :invalid_address}

    iex> X402.Solana.decode_address(nil)
    {:error, :invalid_address}

# `find_program_address`
*since 0.6.0* 

```elixir
@spec find_program_address([binary()], address()) ::
  {:ok, {pubkey(), byte()}}
  | {:error, :invalid_seeds | :invalid_address | :not_found}
```

Finds the first off-curve program address, searching bump seeds 255 down
to 0 — the canonical `find_program_address` from the Solana SDKs.

Returns the raw 32-byte address and the bump seed that produced it.

## Examples

    iex> {:ok, {address, bump}} =
    ...>   X402.Solana.find_program_address(
    ...>     ["hello", "world"],
    ...>     "11111111111111111111111111111111"
    ...>   )
    iex> {X402.Base58.encode(address), bump}
    {"JDC4d5bNdpBPNLHfugxDcuknk6e9cp2xBis5V5v67PGh", 253}

# `lighthouse_program`
*since 0.6.0* 

```elixir
@spec lighthouse_program() :: address()
```

The Lighthouse assertion program address (wallet-injected guard
instructions; allowed as optional instructions by the scheme spec §3.1).

## Examples

    iex> X402.Solana.lighthouse_program()
    "L2TExMFKdjpN9kozasaurPirfHy9P8sbXoAN1qA3S95"

# `memo_program`
*since 0.6.0* 

```elixir
@spec memo_program() :: address()
```

The SPL Memo program address.

## Examples

    iex> X402.Solana.memo_program()
    "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"

# `on_curve?`
*since 0.6.0* 

```elixir
@spec on_curve?(pubkey()) :: boolean()
```

Whether 32 bytes decompress to a point on the Ed25519 curve.

Program derived addresses must *not* be on the curve (so no private key
can exist for them); `create_program_address/2` uses this check. The
algorithm mirrors the reference SDKs' vendored Ed25519 decompression:
mask the sign bit, solve `x^2 = (y^2 - 1) / (d*y^2 + 1)`, and require a
square root to exist (with `x = 0` requiring a zero sign bit).

## Examples

    iex> {:ok, system_program} = X402.Solana.decode_address("11111111111111111111111111111111")
    iex> X402.Solana.on_curve?(system_program)
    true

    iex> {:ok, ata} = X402.Solana.decode_address("DNDTCnZkNk358qDFZd9unHtnrc73SsXcpVWtwJJMrR4B")
    iex> X402.Solana.on_curve?(ata)
    false

# `token_2022_program`
*since 0.6.0* 

```elixir
@spec token_2022_program() :: address()
```

The Token-2022 program address.

## Examples

    iex> X402.Solana.token_2022_program()
    "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"

# `token_program`
*since 0.6.0* 

```elixir
@spec token_program() :: address()
```

The SPL Token program address.

## Examples

    iex> X402.Solana.token_program()
    "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"

# `valid_address?`
*since 0.6.0* 

```elixir
@spec valid_address?(term()) :: boolean()
```

Whether a term is a Base58 string decoding to 32 bytes.

## Examples

    iex> X402.Solana.valid_address?("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")
    true

    iex> X402.Solana.valid_address?("0x036CbD53842c5426634e7929541eC2318f3dCF7e")
    false

---

*Consult [api-reference.md](api-reference.md) for complete listing*
