Embedded Wallet
Embedded Wallet: x402 Payments
Pay x402-gated HTTP APIs from your Host app with the 1Shot embedded wallet — EIP-3009 via eth_signTypedData_v4, wired through @x402/core and @x402/evm.
Overview
x402 is an HTTP payment protocol: a resource server responds with 402 Payment Required, the client signs a payment payload, and retries the request with a payment header. The 1Shot embedded wallet is a plain EOA — compatible with the x402 exact scheme and EIP-3009 (transferWithAuthorization). See x402 wallet compatibility for the full matrix.
Signing happens through eth_signTypedData_v4 on proxy.ethereum. The user approves EIP-712 typed data in the Branding and Signing layers; your Host receives only the signature.
Buyer vs seller — this page is for Host apps whose users pay x402-gated APIs. To facilitate and settle x402 on your backend, see Dev Platform: x402 Facilitator.
Motivation
Most apps still treat payments and API access as separate systems: sign up, store a card, manage subscriptions, then call an API with a bearer token. x402 collapses that into one HTTP round trip — the same request that fetches a resource can carry the payment. For embedded-wallet apps, that means users pay with stablecoins they already hold, without leaving your product or creating a seller-specific account.
Because x402 is HTTP-native, any client that can sign EIP-3009 typed data can buy access — browsers, agents, scripts, or your Host app with the 1Shot wallet. You monetize usage at the edge instead of building billing portals, API key dashboards, and subscription tiers for every new endpoint.
- Pay-to-read articles and reports — gate a single URL or PDF behind a micropayment. The user pays once, the server returns the content, and you skip monthly subscriptions for occasional readers.
- Per-call tools and agent actions — charge per inference, search, scrape, or workflow step. Agents and automations can pay for exactly the calls they make instead of pre-funding a platform balance.
- Premium API endpoints — expose market data, enrichment, or proprietary datasets with HTTP 402 instead of API keys and rate-plan negotiation. CoinGecko's x402 test endpoints are a live example of this pattern.
- In-app unlocks — sell one-off access inside your product: a generated image, an export, a compliance check, or a gated dashboard view, settled in USDC on the chain the seller accepts.
- Agent-to-service commerce — autonomous agents fetch paid resources on behalf of users; the embedded wallet signs EIP-3009 authorizations after the user approves the spend in your Host UI.
What @x402/core and @x402/evm do
The npm packages handle protocol parsing and EIP-3009 payload construction. Your Host app bridges them to the embedded wallet.
@x402/core—x402Clientandx402HTTPClientdecodePAYMENT-REQUIRED, build payment payloads, and encode/decode payment headers.@x402/evm—registerExactEvmSchemeconstructs EIP-3009 EIP-712 typed data and calls yourClientEvmSigner.signTypedData.- Your Host implements — an EIP-1193 bridge from
ClientEvmSignertoproxy.ethereum.request({ method: "eth_signTypedData_v4", ... }). The wallet playground x402 tab is the reference implementation.
Prerequisites
Install
npm install @x402/core @x402/evm @1shotapi/ows-provider viem- User connected via
eth_requestAccountsonproxy.ethereum. - USDC or another stablecoin advertised in the 402
acceptsentry, on the required chain. - Wallet on the correct chain — call
wallet_switchEthereumChainwhen the 402 challenge specifies a different network.
Implement the signer adapter
@x402/evm expects a ClientEvmSigner with address and signTypedData. Forward typed data to the embedded wallet's EIP-1193 provider.
EIP-1193 ClientEvmSigner
import type { EIP1193Provider } from "@1shotapi/ows-provider";
import type { ClientEvmSigner } from "@x402/evm";
/** EIP-712 payloads from @x402/evm use bigint for uint256 fields; JSON-RPC must not. */
function toJsonSafeTypedData(value: unknown): unknown {
if (typeof value === "bigint") return value.toString();
if (Array.isArray(value)) return value.map(toJsonSafeTypedData);
if (value !== null && typeof value === "object") {
return Object.fromEntries(
Object.entries(value as Record<string, unknown>).map(([k, v]) => [
k,
toJsonSafeTypedData(v),
]),
);
}
return value;
}
export function createX402Eip1193Signer(
provider: EIP1193Provider,
address: `0x${string}`,
): ClientEvmSigner {
return {
address,
async signTypedData({ domain, types, primaryType, message }) {
const typedData = toJsonSafeTypedData({
domain,
types,
primaryType,
message,
});
return (await provider.request({
method: "eth_signTypedData_v4",
params: [address, typedData],
})) as `0x${string}`;
},
};
}BigInt shim — @x402/evm passes bigint for uint256 fields. JSON-RPC and postMessage cannot serialize BigInt; convert to decimal strings before the RPC call.
Payment flow in the Host
Use a deliberate two-step UX: fetch payment details, show the user amount/network/asset/recipient, then purchase on confirmation. This matches the playground x402 tab and avoids surprise signing prompts.
Purchase after user confirmation
import { x402Client } from "@x402/core/client";
import { x402HTTPClient } from "@x402/core/http";
import type { PaymentRequired } from "@x402/core/types";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { getAddress } from "viem";
async function purchaseResource({
url,
paymentRequired,
provider,
account,
fetchWithPaymentHeaders,
}: {
url: string;
paymentRequired: PaymentRequired;
provider: EIP1193Provider;
account: string;
fetchWithPaymentHeaders: (
url: string,
headers: Record<string, string>,
) => Promise<Response>;
}) {
const checksumAddress = getAddress(account) as `0x${string}`;
const signer = createX402Eip1193Signer(provider, checksumAddress);
const client = new x402Client();
client.setSpendControls(false); // Host shows its own purchase confirmation.
registerExactEvmScheme(client, { signer });
const httpClient = new x402HTTPClient(client);
const paymentPayload = await httpClient.createPaymentPayload(paymentRequired);
const paymentHeaders = httpClient.encodePaymentSignatureHeader(paymentPayload);
const response = await fetchWithPaymentHeaders(url, paymentHeaders);
if (!response.ok) {
throw new Error(`Paid request failed with HTTP ${response.status}`);
}
return response;
}For server-side / private-key buyers, @x402/fetch offers wrapFetchWithPayment. That path is not suitable for passkey embedded wallets — use the EIP-1193 adapter above instead.
GETthe resource URL → receive HTTP 402 and aPAYMENT-REQUIREDheader (v2) or v1 body.x402HTTPClient.getPaymentRequiredResponse(...)→ parse amount, network, asset, andpayTofor your UI.- User confirms →
createPaymentPayload(paymentRequired)→ wallet EIP-712 consent. encodePaymentSignatureHeader(payload)→ retryGETwithPAYMENT-SIGNATURE/X-PAYMENT.- Parse the response body and optional
PAYMENT-RESPONSEsettlement header.
Browser CORS and proxy pattern
Browsers cannot read cross-origin 402 challenges unless the API sends CORS headers and exposes payment headers via Access-Control-Expose-Headers. Most x402 resources are third-party origins, so a same-origin proxy on your backend is the recommended pattern.
- Your proxy forwards GET requests upstream and returns status, body, and x402 headers to your Host UI.
- On the paid retry, forward
PAYMENT-SIGNATURE/X-PAYMENTupstream. - The playground proxy (
/playground/x402/proxy) is a reference — GET-only upstream, x402 header allowlist, 5 MB cap. Implement an equivalent on your origin; do not call the marketing-site proxy in production.
Supported schemes
- Documented in this guide —
exactscheme with EIP-3009 oneip155:*networks. The playground x402 tab exercises this path end to end. - Permit2 — the wallet supports Permit2 the same way:
@x402/evmsupplies different EIP-712 typed data, and your Host forwards it througheth_signTypedData_v4. No separate wallet capability is required — only the x402 asset-transfer method and scheme registration differ from EIP-3009. - Not covered here yet —
uptoand Solana/non-EVM accepts. See x402 schemes overview for the full protocol matrix.
Analytics
EIP-3009 signing surfaces on proxy.analytics as TypedSign, TypedSignFailed, and TypedSignCancelled. Forward these to your analytics pipeline alongside other wallet events.
See Embedded Wallet: Analytics for the full subscribe → forward → GA4 setup.
Test in the playground
Open the wallet playground → x402 tab. The CoinGecko demo URL is pre-filled. Flow: Fetch payment details → connect wallet → switch network if prompted → Purchase → view the JSON response in the modal.