Skip to content
Get startedGetting started →

Send a transaction with viem

Mera exports an adapter function toViemAccount that adapts a signing session into the account shape viem accepts, so viem signs and broadcasts while the key is owned by the session.

import { getPasskeyPrfOutput } from "@category-labs/mera";
const rpId = location.hostname;
const { prfOutput } = await getPasskeyPrfOutput({ rpId });

The seed and path come from the same mapping Create passkey accounts uses (Keys and accounts introduces the standards).

import { HDKey } from "@scure/bip32";
import { entropyToMnemonic, mnemonicToSeedSync } from "@scure/bip39";
import { wordlist } from "@scure/bip39/wordlists/english.js";
const firstEthereumAccountPath = "m/44'/60'/0'/0/0";
const seed = mnemonicToSeedSync(entropyToMnemonic(prfOutput, wordlist));
const node = HDKey.fromMasterSeed(seed).derive(firstEthereumAccountPath);
if (node.privateKey === null) throw new Error("derivation produced no key");

toViemAccount lives in the @category-labs/mera/viem entry point, which requires the optional viem peer dependency.

import { createSecp256k1SigningSession } from "@category-labs/mera";
import { toViemAccount } from "@category-labs/mera/viem";
const session = createSecp256k1SigningSession({
privateKey: node.privateKey,
});
const account = toViemAccount(session);
import { createWalletClient, http, parseEther } from "viem";
import { sepolia } from "viem/chains";
const client = createWalletClient({
account,
chain: sepolia,
transport: http(),
});
const recipient = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8";
const hash = await client.sendTransaction({
to: recipient,
value: parseEther("0.01"),
});
session.end();

sendTransaction fills the missing transaction fields from the RPC, signs through the session and broadcasts.