TwinKey
transport /api/rpcwagmi v3

wagmi, unchanged

A wagmi v3 config with one TwinKey line: transports: { [chain.id]: custom(twinKeyProvider()) }. The hooks below are the framework's own — useReadContract, useWriteContract, useWaitForTransactionReceipt. The write becomes a native revive.call extrinsic signed with an sr25519 key, and the receipt comes back from the Substrate archive because this chain's EVM log index never sees that call.

Read this honestly: the demo account signs server-side, so this runs in any browser with no extension. A wallet signing in the browser is what the main app does with your own key.

The config that runs

import { createConfig, WagmiProvider, useReadContract, useWriteContract } from "wagmi";
import { custom, defineChain, erc20Abi } from "viem";

const chain = defineChain({ id: 420420777, name: "Portaldot V3 Test", … });

const config = createConfig({
  chains: [chain],
  connectors: [twinKeyConnector()],                    // reports the ss58 account
  transports: { [chain.id]: custom(twinKeyProvider()) },   // <- the only TwinKey line
});

// below this point it is a normal wagmi app
const { data: balance } = useReadContract({ address: TOKEN, abi: erc20Abi, functionName: "balanceOf", args: [H160] });
const { writeContractAsync } = useWriteContract();
const hash = await writeContractAsync({ address: TOKEN, abi: erc20Abi, functionName: "transfer", args: [TO, 10n ** 18n] });

Live wagmi v3

chainId 420420777account not connectedtoken …balance … TKT