MPP.Methods.EVM (mpp v0.6.2)

Copy Markdown View Source

Generic EVM payment method — verifies on-chain ERC-20 or native ETH transfers.

Works on any EVM chain (Ethereum, Base, Polygon, Arbitrum, etc.) by configuring the appropriate RPC endpoint and chain ID. The client broadcasts a transaction, then sends the transaction hash as a credential. The server fetches the receipt via RPC and verifies the transfer matches the charge intent.

Configuration

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

plug MPP.Plug,
  secret_key: "hmac-secret",
  realm: "api.example.com",
  method: MPP.Methods.EVM,
  amount: "1000000",
  currency: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
  method_config: %{
    "rpc_url" => "https://mainnet.infura.io/v3/YOUR_KEY",
    "chain_id" => 1
  }

Config Keys

  • "rpc_url" — (required) JSON-RPC endpoint URL for the target EVM chain
  • "chain_id" — (optional) network chain ID, included in challenge details so the client knows which chain to transact on
  • "req_options" — (optional) merged into the Onchain.RPC call as :req_options (e.g. [plug: {Req.Test, MyMod}]) for testing stubs

Credential Payload

The credential payload map must contain:

  • "hash" — (required) 0x-prefixed transaction hash (32 bytes, 66 hex chars)

Currency Conventions

  • ERC-20 tokens: use the token contract address as currency (e.g., "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" for USDC on Ethereum)
  • Native ETH: use "ETH" or the zero address ("0x0000000000000000000000000000000000000000")

Dependencies

Requires the onchain package (optional dependency) for address validation and Transfer event parsing. The method checks availability at init time via validate_config!/1.

API Functions

FunctionArityDescriptionParam Kinds
challenge_method_details1Return EVM-specific fields (chainId) for the 402 challenge.charge: value
verify2Verify an EVM credential by checking on-chain settlement via transaction receipt.payload: value, charge: value
validate_config!1Validate EVM method_config at init time. Raises on missing rpc_url or unavailable onchain dependency.config: value
method_name0Return the payment method identifier for EVM.-

Summary

Functions

Return EVM-specific fields (chainId) for the 402 challenge.

Return the payment method identifier for EVM.

Validate EVM method_config at init time. Raises on missing rpc_url or unavailable onchain dependency.

Verify an EVM credential by checking on-chain settlement via transaction receipt.

Functions

challenge_method_details(charge)

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

Return EVM-specific fields (chainId) for the 402 challenge.

Parameters

  • charge - Charge struct with method_details optionally containing chain_id (value)

Returns

Map with chainId key, or nil if no chain_id configured (map_or_nil)

# descripex:contract
%{
  params: %{
    charge: %{
      description: "Charge struct with method_details optionally containing `chain_id`",
      kind: :value
    }
  },
  returns: %{
    type: :map_or_nil,
    description: "Map with `chainId` key, or `nil` if no `chain_id` configured"
  }
}

method_name()

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

Return the payment method identifier for EVM.

# descripex:contract
%{}

validate_config!(config)

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

Validate EVM method_config at init time. Raises on missing rpc_url or unavailable onchain dependency.

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 an EVM credential by checking on-chain settlement via transaction receipt.

Parameters

  • payload - Credential payload map with "hash" (0x-prefixed transaction hash) (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 `\"hash\"` (0x-prefixed transaction hash)",
      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"
  }
}