"""Optional, explicit x402 signing with a caller-supplied authorized signer. This module neither loads wallet credentials nor sends transactions or HTTP requests. Use the callback with ``FarsideClient.pay`` so the client's payment policy is checked before a signature is requested. """ from __future__ import annotations from collections.abc import Callable from typing import Any def make_x402_signer(existing_signer: Any) -> Callable[[dict[str, Any]], str]: """Adapt an existing account or ``ClientEvmSigner`` to ``client.pay``. The caller supplies an already authorized ``eth_account`` account or an x402 ``ClientEvmSigner`` with ``address`` and ``sign_typed_data``. The SDK handles account wrapping; this adapter does not accept keys or seed phrases. Install the optional ``x402`` extra to use this function. Creating the callback does not sign anything. Calling it creates an EIP-3009 payment authorization. The client saves that limited authorization in its protected local state before submission. Recovery is unsigned; an explicit ``pay`` retry can reuse exactly that authorization without signing again. No wallet key or seed phrase is stored by the adapter or the client. """ if isinstance(existing_signer, (str, bytes, bytearray)) or existing_signer is None: raise TypeError("Pass an already authorized account or signer object.") try: from x402 import x402ClientSync from x402.http.utils import encode_payment_signature_header from x402.mechanisms.evm.exact import ExactEvmScheme from x402.schemas import PaymentRequired except ImportError: raise ImportError( "Install farside-client[x402] to use make_x402_signer." ) from None try: address = existing_signer.address can_sign = callable(existing_signer.sign_typed_data) except Exception: raise TypeError("Pass an already authorized account or signer object.") from None if not isinstance(address, str) or not address.startswith("0x") or not can_sign: raise TypeError("Pass an already authorized account or signer object.") client = x402ClientSync() client.register("eip155:8453", ExactEvmScheme(signer=existing_signer)) def sign(offer: dict[str, Any]) -> str: # ExactEvmScheme also implements Permit2, which can ask advanced signers # for RPC reads or approval signatures. This adapter deliberately uses # only the offline EIP-3009 flow advertised by farside. try: requirements = PaymentRequired.model_validate(offer) except (TypeError, ValueError): raise ValueError("Invalid x402 payment offer.") from None if requirements.x402_version != 2 or len(requirements.accepts) != 1: raise ValueError("Only a single x402 v2 EIP-3009 offer is supported.") accepted = requirements.accepts[0] if ( accepted.scheme != "exact" or str(accepted.network) != "eip155:8453" or accepted.extra.get("assetTransferMethod") != "eip3009" ): raise ValueError("Only exact Base EIP-3009 offers are supported.") try: payload = client.create_payment_payload(requirements) return encode_payment_signature_header(payload) except Exception: raise ValueError("Payment authorization could not be created.") from None return sign