MPP.Methods.Solana (mpp v0.14.0)

Copy Markdown View Source

Solana payment method — verifies native SOL and SPL token charge payments.

Three credential types are supported, matching draft-solana-charge-00:

  • type="transaction" (pull, default) — the client sends signed legacy transaction bytes; the server optionally co-signs as fee payer, simulates, broadcasts, and waits for confirmation.
  • type="signature" (push) — the client broadcasts the transaction and sends the confirmed signature; the server fetches it via RPC and matches the transfer against the charge.
  • type="bundle" (confidential) — the client sends ordered proof setup, Token-2022 confidential transfer, and proof close transactions. The server confirms the encrypted amount with its recipient ElGamal key.

Configuration

Pass Solana-specific config via :method_config in MPP.Plug opts:

plug MPP.Plug,
  secret_key: "hmac-secret",
  realm: "api.example.com",
  method: MPP.Methods.Solana,
  amount: "10000000",
  currency: "sol",
  recipient: "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
  method_config: %{
    "rpc_url" => "https://api.devnet.solana.com",
    "network" => "devnet"
  }

Config Keys

  • "rpc_url" — (required) Solana JSON-RPC endpoint URL
  • "network" — (optional) "mainnet", "devnet", or "localnet"; advertised in challenge details, defaults to "mainnet"
  • "decimals" — (required for SPL) token decimal count advertised to clients
  • "token_program" — (optional) base58 Token or Token-2022 program id
  • "fee_payer" — (optional) server fee sponsorship, defaults to false
  • "fee_payer_private_key" — (required when fee_payer: true) Ed25519 seed as hex, base58, or a Solana CLI JSON keypair
  • "fee_payer_key" — (optional) base58 fee-payer pubkey; derived from the private key when omitted
  • "splits" — (optional) at most 8 extra payment legs (recipient, amount, optional memo, optional ataCreationRequired)
  • "store" — (optional) replay-dedup store, on by default (see MPP.Methods.EVM for the same contract). Pass store: false to opt out
  • "req_options" — (optional) merged into Cartouche.Solana.RPC calls (e.g. [plug: {Req.Test, MyMod}]) for testing stubs
  • "wait_for_confirmation" — (optional) when false, pull mode broadcasts without waiting for confirmation. Default true
  • "max_compute_unit_limit" / "max_compute_unit_price" — (optional) fee-payer ceilings for compute-budget instructions
  • "confidential" — (optional) enables the Token-2022 confidential profile
  • "recipient_elgamal_secret_key" — (required for confidential) base64 canonical scalar for the recipient confidential token account
  • "max_bundle_transactions" — (optional) confidential bundle bound; defaults to 8

Credential Payload

  • "type" => "transaction", "transaction" => "<base64>" — signed legacy transaction bytes (max 1232 decoded)
  • "type" => "signature", "signature" => "<base58>" — confirmed transaction signature
  • "type" => "bundle", "transactions" => ["<base64>", ...] — ordered confidential transaction bundle

Currency

  • Native SOL: "sol"
  • SPL tokens: the base58 mint address

Dependencies

Uses Cartouche.Solana (RPC, legacy transaction codec, System/Token/ATA programs) already in the on-chain stack.

API Functions

FunctionArityDescriptionParam Kinds
challenge_method_details1Return Solana-specific fields (network, credentialTypes, feePayer, optional decimals/tokenProgram/splits) for the 402 challenge.charge: value
verify2Verify a Solana credential by checking on-chain settlement.payload: value, charge: value
validate_config!1Validate Solana method_config at init time. Raises on missing rpc_url or invalid fee-payer / splits config.config: value
credential_types0Return the Solana charge payload types: transaction, signature, and bundle.-
method_name0Return the payment method identifier for Solana.-

Summary

Functions

Return Solana-specific fields (network, credentialTypes, feePayer, optional decimals/tokenProgram/splits) for the 402 challenge.

Return the Solana charge payload types: transaction, signature, and bundle.

Return the payment method identifier for Solana.

Validate Solana method_config at init time. Raises on missing rpc_url or invalid fee-payer / splits config.

Verify a Solana credential by checking on-chain settlement.

Functions

challenge_method_details(charge)

@spec challenge_method_details(MPP.Method.intent()) :: map() | nil
@spec challenge_method_details(MPP.Intents.Charge.t()) :: map()

Return Solana-specific fields (network, credentialTypes, feePayer, optional decimals/tokenProgram/splits) for the 402 challenge.

Parameters

  • charge - Charge struct with method_details containing Solana method_config keys (value)

Returns

Public challenge methodDetails; never includes rpc_url, keys, or store (map)

# descripex:contract
%{
  params: %{
    charge: %{
      description: "Charge struct with method_details containing Solana method_config keys",
      kind: :value
    }
  },
  returns: %{
    type: :map,
    description: "Public challenge methodDetails; never includes rpc_url, keys, or store"
  }
}

credential_types()

@spec credential_types() :: [String.t()]
@spec credential_types() :: [String.t()]

Return the Solana charge payload types: transaction, signature, and bundle.

# descripex:contract
%{}

method_name()

@spec method_name() :: String.t()

Return the payment method identifier for Solana.

# descripex:contract
%{}

validate_config!(config)

@spec validate_config!(map()) :: :ok
@spec validate_config!(map()) :: :ok

Validate Solana method_config at init time. Raises on missing rpc_url or invalid fee-payer / splits config.

Parameters

  • config - method_config map to validate (value)

Returns

:ok on success, raises ArgumentError on missing keys (atom)

# descripex:contract
%{
  params: %{
    config: %{description: "method_config map to validate", kind: :value}
  },
  returns: %{
    type: :atom,
    description: "`:ok` on success, raises `ArgumentError` on missing keys"
  }
}

verify(payload, charge)

@spec verify(map(), MPP.Intents.Charge.t()) ::
  {:ok, MPP.Receipt.t()} | {:error, MPP.Errors.t()}

Verify a Solana credential by checking on-chain settlement.

Parameters

  • payload - Credential payload map with "type" ("transaction", "signature", or "bundle") and the corresponding proof field (value)
  • charge - Charge intent struct with amount, currency, recipient, and method_details (including rpc_url) (value)

Returns

{:ok, receipt} on success, {:error, error} on failure (tagged_tuple)

Errors

  • :invalid_payload
  • :verification_failed
# descripex:contract
%{
  params: %{
    payload: %{
      description: "Credential payload map with `\"type\"` (`\"transaction\"`, `\"signature\"`, or `\"bundle\"`) and the corresponding proof field",
      kind: :value
    },
    charge: %{
      description: "Charge intent struct with amount, currency, recipient, and method_details (including `rpc_url`)",
      kind: :value
    }
  },
  errors: [:invalid_payload, :verification_failed],
  returns: %{
    type: :tagged_tuple,
    description: "`{:ok, receipt}` on success, `{:error, error}` on failure"
  }
}