# `Cartouche.Solana.PDA`
[🔗](https://github.com/zenhive/cartouche/blob/main/lib/cartouche/solana/pda.ex#L1)

Program Derived Addresses (PDAs) for Solana.

A PDA is an address derived from seeds and a program ID that is guaranteed
to NOT be on the Ed25519 curve (no private key can sign for it). Only the
owning program can "sign" for a PDA via cross-program invocation.

## Examples

    iex> {:ok, {address, bump}} = Cartouche.Solana.PDA.find_program_address(["hello"], <<0::256>>)
    iex> byte_size(address)
    32
    iex> bump >= 0 and bump <= 255
    true

## API Functions
| Function | Arity | Description | Param Kinds |
| --- | --- | --- | --- |
| `on_curve?` | 1 | Check whether 32 bytes represent an on-curve Ed25519 public key. | `pub_key: value` |
| `create_program_address` | 2 | Create a single Solana program address candidate from seeds and a program id. | `seeds: value`, `program_id: value` |
| `find_program_address!` | 2 | Find a Solana program-derived address from seeds and raise if no bump is valid. | `seeds: value`, `program_id: value` |
| `find_program_address` | 2 | Find a Solana program-derived address from seeds and a program id. | `seeds: value`, `program_id: value` |

# `create_program_address`

```elixir
@spec create_program_address([binary()], &lt;&lt;_::256&gt;&gt;) ::
  {:ok, &lt;&lt;_::256&gt;&gt;} | {:error, :on_curve}
```

Create a program address from seeds (including bump) and program ID.

Returns `{:ok, address}` if the result is off-curve, or `{:error, :on_curve}`
if the candidate is on the Ed25519 curve.

This is the single-attempt version where the caller provides the bump seed
as part of the seeds list.

## Examples

    iex> {address, bump} = Cartouche.Solana.PDA.find_program_address!(["test"], <<0::256>>)
    iex> {:ok, ^address} = Cartouche.Solana.PDA.create_program_address(["test", <<bump>>], <<0::256>>)
    iex> byte_size(address)
    32

# `find_program_address`

```elixir
@spec find_program_address([binary()], &lt;&lt;_::256&gt;&gt;) ::
  {:ok, {&lt;&lt;_::256&gt;&gt;, non_neg_integer()}} | {:error, :no_valid_pda}
```

Find a program-derived address from seeds and a program ID.

Tries bump seeds from 255 down to 0, returning the first off-curve result.

## Examples

    iex> {:ok, {_address, bump}} = Cartouche.Solana.PDA.find_program_address(["hello"], <<0::256>>)
    iex> bump >= 0 and bump <= 255
    true

# `find_program_address!`

```elixir
@spec find_program_address!([binary()], &lt;&lt;_::256&gt;&gt;) :: {&lt;&lt;_::256&gt;&gt;, non_neg_integer()}
```

Like `find_program_address/2`, but raises on failure.

## Examples

    iex> {_address, bump} = Cartouche.Solana.PDA.find_program_address!(["hello"], <<0::256>>)
    iex> bump >= 0 and bump <= 255
    true

# `on_curve?`

```elixir
@spec on_curve?(&lt;&lt;_::256&gt;&gt;) :: boolean()
```

Check if 32 bytes represent a valid Ed25519 public key (on the curve).

Uses Ed25519 compressed point decompression: interprets the bytes as a
y-coordinate and checks if the corresponding x² is a quadratic residue
mod p. Uses `:crypto.mod_pow/3` for efficient modular exponentiation.

## Examples

    iex> valid_pub = Base.decode16!("D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A")
    iex> Cartouche.Solana.PDA.on_curve?(valid_pub)
    true

    iex> Cartouche.Solana.PDA.on_curve?(<<0::256>>)
    true

---

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