Skip to content

mera

Creates a wagmi connector for a passkey account. Connecting runs a passkey ceremony, which shows a prompt. By default, every signing request runs its own ceremony and ends its signing session once it settles, so no key stays in memory between requests. The connector lives in the @category-labs/mera/wagmi entry point, which requires @wagmi/core (^3.0.0) and viem (^2.28.0) as optional peer dependencies.

import {
function mera({ rp, accountIndex, webAuthnClient, keepSessionOpen, }: MeraOptions): CreateConnectorFn<Provider, Properties, StorageItem>

Creates a wagmi connector for a passkey account: an EVM account whose key derives from a passkey's PRF output.

@param ― options - Relying party, account index, and WebAuthn client.

@returns ― A wagmi connector function with id "mera".

@remarks ―

Connecting runs one passkey ceremony, or two when it creates a passkey on an authenticator that evaluates the PRF only at sign-in. Signing requests run one at a time. Each runs its own ceremony, restricted to the stored credential, and ends its signing session once it settles. With keepSessionOpen, the session that connecting opens, or that the first signing request after a page load opens, stays open and signs later requests without a prompt. A passkey that opens a different address is refused.

The connector keeps the account's address and credential metadata in wagmi's storage, never key material, so reconnecting after a page load shows no prompt. It ignores a record stored for another rp.id or accountIndex. The connector starts on the config's first chain.

The provider signs personal_sign, eth_signTypedData_v4, and eth_sendTransaction itself, and refuses typed data whose domain names another chain. viem fills, signs, and sends each transaction through the config's transport for the current chain, or the chain's default RPC when the config has none. Transactions sent together get consecutive nonces. Other signing and wallet_ methods, including wallet_sendCalls, reject with MethodNotSupportedRpcError (code -32004); other methods go to the transport. A ceremony that fails with PASSKEY_CANCELLED, such as a dismissed or timed-out prompt, rejects with UserRejectedRequestError (code 4001); other ceremony failures reject with their MeraError, which viem wraps for signing requests.

@throws ― MeraError with code INPUT_INVALID when accountIndex is not an integer from 0 to 2^31 - 1.

mera
} from "@category-labs/mera/wagmi";
import {
function mera({ rp, accountIndex, webAuthnClient, keepSessionOpen, }: MeraOptions): CreateConnectorFn<Provider, Properties, StorageItem>

Creates a wagmi connector for a passkey account: an EVM account whose key derives from a passkey's PRF output.

@param ― options - Relying party, account index, and WebAuthn client.

@returns ― A wagmi connector function with id "mera".

@remarks ―

Connecting runs one passkey ceremony, or two when it creates a passkey on an authenticator that evaluates the PRF only at sign-in. Signing requests run one at a time. Each runs its own ceremony, restricted to the stored credential, and ends its signing session once it settles. With keepSessionOpen, the session that connecting opens, or that the first signing request after a page load opens, stays open and signs later requests without a prompt. A passkey that opens a different address is refused.

The connector keeps the account's address and credential metadata in wagmi's storage, never key material, so reconnecting after a page load shows no prompt. It ignores a record stored for another rp.id or accountIndex. The connector starts on the config's first chain.

The provider signs personal_sign, eth_signTypedData_v4, and eth_sendTransaction itself, and refuses typed data whose domain names another chain. viem fills, signs, and sends each transaction through the config's transport for the current chain, or the chain's default RPC when the config has none. Transactions sent together get consecutive nonces. Other signing and wallet_ methods, including wallet_sendCalls, reject with MethodNotSupportedRpcError (code -32004); other methods go to the transport. A ceremony that fails with PASSKEY_CANCELLED, such as a dismissed or timed-out prompt, rejects with UserRejectedRequestError (code 4001); other ceremony failures reject with their MeraError, which viem wraps for signing requests.

@throws ― MeraError with code INPUT_INVALID when accountIndex is not an integer from 0 to 2^31 - 1.

mera
} from "@category-labs/mera/wagmi";
import {
function connect<config extends Config, connector extends Connector | CreateConnectorFn, withCapabilities extends boolean = false>(config: config, parameters: ConnectParameters<config, connector, withCapabilities>): Promise<ConnectReturnType<config, connector, withCapabilities>>
connect
,
function createConfig<const chains extends readonly [Chain, ...Chain[]], transports extends Record<chains[number]["id"], Transport>, const connectorFns extends readonly CreateConnectorFn[]>(parameters: CreateConfigParameters<chains, transports, connectorFns>): Config<chains, transports, connectorFns>
createConfig
,
function http<rpcSchema extends RpcSchema | undefined = undefined, raw extends boolean = false>(url?: string | undefined, config?: HttpTransportConfig<rpcSchema, raw>): HttpTransport<rpcSchema, raw>

@description Creates a HTTP transport that connects to a JSON-RPC API.

http
} from "@wagmi/core";
import {
const sepolia: {
blockExplorers: {
readonly default: {
readonly name: "Etherscan";
readonly url: "https://sepolia.etherscan.io";
readonly apiUrl: "https://api-sepolia.etherscan.io/api";
};
};
blockTime?: number | undefined | undefined;
contracts: {
readonly multicall3: {
readonly address: "0xca11bde05977b3631167028862be2a173976ca11";
readonly blockCreated: 751532;
};
readonly ensUniversalResolver: {
readonly address: "0xeeeeeeee14d718c2b47d9923deab1335e144eeee";
readonly blockCreated: 8928790;
};
};
... 16 more ...;
verifyHash?: ((client: Client<...>, parameters: VerifyHashParameters) => Promise<boolean>) | undefined;
}
sepolia
} from "viem/chains";
const
const config: Config<readonly [{
blockExplorers: {
readonly default: {
readonly name: "Etherscan";
readonly url: "https://sepolia.etherscan.io";
readonly apiUrl: "https://api-sepolia.etherscan.io/api";
};
};
blockTime?: number | undefined | undefined;
contracts: {
readonly multicall3: {
readonly address: "0xca11bde05977b3631167028862be2a173976ca11";
readonly blockCreated: 751532;
};
readonly ensUniversalResolver: {
readonly address: "0xeeeeeeee14d718c2b47d9923deab1335e144eeee";
readonly blockCreated: 8928790;
};
};
... 16 more ...;
verifyHash?: ((client: Client<...>, parameters: VerifyHashParameters) => Promise<boolean>) | undefined;
}], {
...;
}, readonly [...]>
config
=
createConfig<readonly [{
blockExplorers: {
readonly default: {
readonly name: "Etherscan";
readonly url: "https://sepolia.etherscan.io";
readonly apiUrl: "https://api-sepolia.etherscan.io/api";
};
};
blockTime?: number | undefined | undefined;
contracts: {
readonly multicall3: {
readonly address: "0xca11bde05977b3631167028862be2a173976ca11";
readonly blockCreated: 751532;
};
readonly ensUniversalResolver: {
readonly address: "0xeeeeeeee14d718c2b47d9923deab1335e144eeee";
readonly blockCreated: 8928790;
};
};
... 16 more ...;
verifyHash?: ((client: Client<...>, parameters: VerifyHashParameters) => Promise<boolean>) | undefined;
}], {
...;
}, readonly [...]>(parameters: CreateConfigParameters<...>): Config<...>
createConfig
({
chains: readonly [{
blockExplorers: {
readonly default: {
readonly name: "Etherscan";
readonly url: "https://sepolia.etherscan.io";
readonly apiUrl: "https://api-sepolia.etherscan.io/api";
};
};
blockTime?: number | undefined | undefined;
contracts: {
readonly multicall3: {
readonly address: "0xca11bde05977b3631167028862be2a173976ca11";
readonly blockCreated: 751532;
};
readonly ensUniversalResolver: {
readonly address: "0xeeeeeeee14d718c2b47d9923deab1335e144eeee";
readonly blockCreated: 8928790;
};
};
... 16 more ...;
verifyHash?: ((client: Client<...>, parameters: VerifyHashParameters) => Promise<boolean>) | undefined;
}]
chains
: [
const sepolia: {
blockExplorers: {
readonly default: {
readonly name: "Etherscan";
readonly url: "https://sepolia.etherscan.io";
readonly apiUrl: "https://api-sepolia.etherscan.io/api";
};
};
blockTime?: number | undefined | undefined;
contracts: {
readonly multicall3: {
readonly address: "0xca11bde05977b3631167028862be2a173976ca11";
readonly blockCreated: 751532;
};
readonly ensUniversalResolver: {
readonly address: "0xeeeeeeee14d718c2b47d9923deab1335e144eeee";
readonly blockCreated: 8928790;
};
};
... 16 more ...;
verifyHash?: ((client: Client<...>, parameters: VerifyHashParameters) => Promise<boolean>) | undefined;
}
sepolia
],
connectors?: readonly [CreateConnectorFn<Provider, Properties, StorageItem>] | undefined
connectors
: [
function mera({ rp, accountIndex, webAuthnClient, keepSessionOpen, }: MeraOptions): CreateConnectorFn<Provider, Properties, StorageItem>

Creates a wagmi connector for a passkey account: an EVM account whose key derives from a passkey's PRF output.

@param ― options - Relying party, account index, and WebAuthn client.

@returns ― A wagmi connector function with id "mera".

@remarks ―

Connecting runs one passkey ceremony, or two when it creates a passkey on an authenticator that evaluates the PRF only at sign-in. Signing requests run one at a time. Each runs its own ceremony, restricted to the stored credential, and ends its signing session once it settles. With keepSessionOpen, the session that connecting opens, or that the first signing request after a page load opens, stays open and signs later requests without a prompt. A passkey that opens a different address is refused.

The connector keeps the account's address and credential metadata in wagmi's storage, never key material, so reconnecting after a page load shows no prompt. It ignores a record stored for another rp.id or accountIndex. The connector starts on the config's first chain.

The provider signs personal_sign, eth_signTypedData_v4, and eth_sendTransaction itself, and refuses typed data whose domain names another chain. viem fills, signs, and sends each transaction through the config's transport for the current chain, or the chain's default RPC when the config has none. Transactions sent together get consecutive nonces. Other signing and wallet_ methods, including wallet_sendCalls, reject with MethodNotSupportedRpcError (code -32004); other methods go to the transport. A ceremony that fails with PASSKEY_CANCELLED, such as a dismissed or timed-out prompt, rejects with UserRejectedRequestError (code 4001); other ceremony failures reject with their MeraError, which viem wraps for signing requests.

@throws ― MeraError with code INPUT_INVALID when accountIndex is not an integer from 0 to 2^31 - 1.

mera
({
rp: PasskeyRelyingParty

Relying party the passkeys belong to. rp.name is shown when a passkey is created.

rp
: {
id: string

Relying party ID: the host the passkey is scoped to.

id
: "account.example.com",
name: string

Relying party name the authenticator may show.

name
: "Example" } })],
transports: {
11155111: HttpTransport<undefined, false>;
}
transports
: { [
const sepolia: {
blockExplorers: {
readonly default: {
readonly name: "Etherscan";
readonly url: "https://sepolia.etherscan.io";
readonly apiUrl: "https://api-sepolia.etherscan.io/api";
};
};
blockTime?: number | undefined | undefined;
contracts: {
readonly multicall3: {
readonly address: "0xca11bde05977b3631167028862be2a173976ca11";
readonly blockCreated: 751532;
};
readonly ensUniversalResolver: {
readonly address: "0xeeeeeeee14d718c2b47d9923deab1335e144eeee";
readonly blockCreated: 8928790;
};
};
... 16 more ...;
verifyHash?: ((client: Client<...>, parameters: VerifyHashParameters) => Promise<boolean>) | undefined;
}
sepolia
.
id: 11155111
id
]:
http<undefined, false>(url?: string | undefined, config?: HttpTransportConfig<undefined, false> | undefined): HttpTransport<undefined, false>

@description Creates a HTTP transport that connects to a JSON-RPC API.

http
() },
});
const [
const passkey: Connector<CreateConnectorFn<Provider, Properties, StorageItem>>
passkey
] =
const config: Config<readonly [{
blockExplorers: {
readonly default: {
readonly name: "Etherscan";
readonly url: "https://sepolia.etherscan.io";
readonly apiUrl: "https://api-sepolia.etherscan.io/api";
};
};
blockTime?: number | undefined | undefined;
contracts: {
readonly multicall3: {
readonly address: "0xca11bde05977b3631167028862be2a173976ca11";
readonly blockCreated: 751532;
};
readonly ensUniversalResolver: {
readonly address: "0xeeeeeeee14d718c2b47d9923deab1335e144eeee";
readonly blockCreated: 8928790;
};
};
... 16 more ...;
verifyHash?: ((client: Client<...>, parameters: VerifyHashParameters) => Promise<boolean>) | undefined;
}], {
11155111: HttpTransport<undefined, false>;
}, readonly [...]>
config
.
connectors: readonly [Connector<CreateConnectorFn<Provider, Properties, StorageItem>>]
connectors
;
await
connect<Config<readonly [{
blockExplorers: {
readonly default: {
readonly name: "Etherscan";
readonly url: "https://sepolia.etherscan.io";
readonly apiUrl: "https://api-sepolia.etherscan.io/api";
};
};
blockTime?: number | undefined | undefined;
contracts: {
readonly multicall3: {
readonly address: "0xca11bde05977b3631167028862be2a173976ca11";
readonly blockCreated: 751532;
};
readonly ensUniversalResolver: {
readonly address: "0xeeeeeeee14d718c2b47d9923deab1335e144eeee";
readonly blockCreated: 8928790;
};
};
... 16 more ...;
verifyHash?: ((client: Client<...>, parameters: VerifyHashParameters) => Promise<boolean>) | undefined;
}], {
...;
}, readonly [...]>, Connector<...>, false>(config: Config<...>, parameters: ConnectParameters<...>): Promise<...>
connect
(
const config: Config<readonly [{
blockExplorers: {
readonly default: {
readonly name: "Etherscan";
readonly url: "https://sepolia.etherscan.io";
readonly apiUrl: "https://api-sepolia.etherscan.io/api";
};
};
blockTime?: number | undefined | undefined;
contracts: {
readonly multicall3: {
readonly address: "0xca11bde05977b3631167028862be2a173976ca11";
readonly blockCreated: 751532;
};
readonly ensUniversalResolver: {
readonly address: "0xeeeeeeee14d718c2b47d9923deab1335e144eeee";
readonly blockCreated: 8928790;
};
};
... 16 more ...;
verifyHash?: ((client: Client<...>, parameters: VerifyHashParameters) => Promise<boolean>) | undefined;
}], {
11155111: HttpTransport<undefined, false>;
}, readonly [...]>
config
, {
connector: CreateConnectorFn | Connector<CreateConnectorFn<Provider, Properties, StorageItem>>
connector
:
const passkey: Connector<CreateConnectorFn<Provider, Properties, StorageItem>>
passkey
});
  • Type: { id: string; name: string }
  • Required

Relying party the passkeys belong to. rp.name is shown when connect creates a passkey.

  • Type: number
  • Optional; defaults to 0

Account number passed to deriveEvmPrivateKey. An integer from 0 to 2³¹ − 1.

  • Type: WebAuthnClient
  • Optional; defaults to the built-in browser client

Client that runs the ceremonies. WebAuthnClient covers supplying one for a runtime without navigator.credentials.

  • Type: boolean
  • Optional; defaults to false

Keeps the signing session that connecting opens, or that the first signing request after a page load opens, so later requests sign without a prompt. The derived key stays in memory until disconnect, a connect to another account, or a page unload. The signing sessions page covers the trade-off.

A wagmi CreateConnectorFn for a connector with the id "mera" and the name "Passkey".

connect({ connector }) asks for any passkey the relying party has and connects its account. connect({ connector, create: { user } }) creates a passkey for user, the { name, displayName } pair createPasskeyWithPrfOutput takes, and connects its account. create runs one ceremony, or two on authenticators that evaluate the PRF only at sign-in. When the second prompt of a create is dismissed, the new passkey stays on the authenticator, and a later connect without create offers it. connect({ connector, immediate: true }) asks only for passkeys already on this device, and rejects at once with UserRejectedRequestError when there is none; getPasskeyPrfOutput lists where immediate requests run.

Reconnecting after a page load restores the stored account and shows no prompt. Signing requests run one at a time. Each runs a ceremony restricted to the stored credential, so requests made together prompt one after another. With keepSessionOpen, signing after a connect shows no prompt, and only the first request after a page load runs one. A passkey that opens a different address than the stored one is refused with UnauthorizedProviderError (code 4100).

The provider that getProvider returns answers these methods:

  • eth_accounts, eth_requestAccounts, and eth_chainId.
  • wallet_switchEthereumChain, for the config’s chains.
  • personal_sign and eth_signTypedData_v4.
  • eth_sendTransaction, for legacy, EIP-2930, and EIP-1559 transactions. viem fills, signs, and sends each one through the config’s transport for the current chain, or the chain’s default RPC when the config has none. Transactions sent at the same time get consecutive nonces.

Other eth_sign*, personal_*, and wallet_* methods reject with MethodNotSupportedRpcError (code -32004). That includes EIP-5792 wallet_sendCalls; viem’s sendCalls with experimental_fallback: true sends one transaction per call instead. A transaction with a blob, an EIP-7702 authorization, or a field outside the standard request fields, and typed data whose domain chainId is not the current chain, reject with InvalidParamsRpcError (code -32602) before any prompt. Every other method goes to the transport.

  • INPUT_INVALID: accountIndex is not an integer from 0 to 2³¹ − 1, which mera throws when called, or connect gets both create and immediate.

A dismissed or timed-out prompt rejects connect or the signing request with viem’s UserRejectedRequestError (code 4001). Browsers give the same NotAllowedError when the page lacks focus or runs in a cross-origin iframe without the WebAuthn permissions, so those cases reject the same way.

Every other ceremony failure rejects connect with its MeraError, such as PRF_UNAVAILABLE or PASSKEY_OPERATION_FAILED. A signing request reaches the connector through viem, which wraps the MeraError in its own error; error.walk((cause) => cause instanceof MeraError) returns it.

The connector keeps the account’s address and credential metadata, and no key material, under mera.account in the storage passed to createConfig. The record names its rp.id and accountIndex, and reconnecting ignores a record for other values. disconnect deletes the record. With storage: null, the account stays connected until the page unloads.

The connector starts on the config’s first chain after a page load.