Quickstart
Ship a working sign-in + multi-chain wallet in 10 minutes with @oviato/connect.
Build a working React app with passkey sign-in, native sends (BTC + ETH), and a token transfer — all in one file.
This guide uses @oviato/connect. Not on React? See
Chain-Agnostic → Overview for the
@oviato/sdk version.
Prerequisites
- An
appIdfrom the Developer Dashboard (free to create) - Node 18+ and React 18+
- A device with a passkey — iPhone/iPad with Face ID, Mac with Touch ID, Android with fingerprint, or a FIDO2 security key
1. Install
pnpm add @oviato/connectnpm install @oviato/connectyarn add @oviato/connect2. Wrap your app in the provider
'use client';
import { OviConnectProvider } from '@oviato/connect/client';
import '@oviato/connect/client.css';
export function Providers({ children }: { children: React.ReactNode }) {
return (
<OviConnectProvider appId="your-app-id-here" theme="auto">
{children}
</OviConnectProvider>
);
}import { Providers } from './providers';
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}import React from 'react';
import ReactDOM from 'react-dom/client';
import { OviConnectProvider } from '@oviato/connect/client';
import '@oviato/connect/client.css';
import App from './App';
ReactDOM.createRoot(document.getElementById('root')!).render(
<OviConnectProvider appId="your-app-id-here" theme="auto">
<App />
</OviConnectProvider>,
);Replace your-app-id-here with the appId from your dashboard. The
provider's appId is non-optional — the SDK refuses to initialize without
it.
3. Add a Connect button
The fastest way — drop in our pre-built button, pick a network per button.
'use client';
import { ConnectButton, useOviConnect } from '@oviato/connect/client';
import { Network } from '@oviato/connect';
export default function Page() {
const { session, disconnect } = useOviConnect();
if (!session) {
return (
<div className="grid gap-3 p-6">
<ConnectButton text="Connect to Bitcoin" network={Network.BTCMAINNET} />
<ConnectButton text="Connect to Base" network={Network.BASEMAINNET} />
<ConnectButton text="Connect to Ethereum" network={Network.ETHMAINNET} />
</div>
);
}
return (
<div className="p-6">
<p>Connected as {session.username}</p>
<p>Address: {session.address}</p>
<p>Network: {session.network}</p>
<button onClick={disconnect}>Disconnect</button>
</div>
);
}Click one, approve the passkey, done. The session object is now populated with the user's address, network, public keys, and auth token.
4. Send your first transaction
Every chain supports sendTransfer — pass the recipient + amount in the chain's smallest unit.
import { sendTransfer } from '@oviato/connect';
async function sendBitcoin() {
const r = await sendTransfer({
recipient: {
address: 'bc1q...',
amount: 10000, // satoshis (0.0001 BTC)
},
fee: 'auto',
});
if (r.status === 'success' && r.type === 'sendTransferSuccess') {
console.log('TxID:', r.data.txid);
console.log('Explorer:', r.data.explorerUrl);
} else if (r.status === 'error') {
console.error(r.message);
}
}import { sendTransfer } from '@oviato/connect';
async function sendEth() {
const r = await sendTransfer({
recipient: {
address: '0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6',
amount: 10n ** 15n, // wei (0.001 ETH) — bigint for overflow safety
},
fee: 'auto',
});
if (r.status === 'success' && r.type === 'sendTransferSuccess') {
console.log('Hash:', r.data.txid);
console.log('Explorer:', r.data.explorerUrl);
}
}A popup opens with the full transaction details. The user approves with their passkey, the bridge signs server-side, and the tx broadcasts. You get back a hash + explorer URL.
5. Transfer a token (EVM only)
Use evm.writeContract to call any contract method. Here's USDC transfer:
import { evm } from '@oviato/connect';
const ERC20_ABI = [
{
type: 'function',
name: 'transfer',
inputs: [
{ name: 'to', type: 'address' },
{ name: 'amount', type: 'uint256' },
],
outputs: [{ type: 'bool' }],
stateMutability: 'nonpayable',
},
] as const;
async function transferUSDC() {
const r = await evm.writeContract({
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC on Ethereum
abi: ERC20_ABI,
functionName: 'transfer',
args: ['0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6', 1_000_000n], // 1 USDC (6 decimals)
});
if (r.status === 'success' && r.type === 'sendTransactionSuccess') {
console.log('Hash:', r.data.hash);
}
}The bridge auto-detects this is an ERC-20 transfer (via the ABI + known-contracts registry) and renders a dedicated "Transfer Token" confirmation sheet — no raw hex shown to the user.
6. Wait for confirmation
import { evm } from '@oviato/connect';
const receipt = await evm.waitForTransactionReceipt({ hash });
if (receipt?.status === 'success') {
console.log('Mined in block', receipt.blockNumber);
}Defaults adapt to the chain: 1 confirmation + 2s polling on L2s, 3 confirmations + 4s polling on Ethereum mainnet.
That's it
You now have:
- Passkey auth
- Multi-chain connect
- Native + ERC-20 sends
- Receipt polling
Not bad for 20 lines of code.
What to learn next
- Core Concepts — the mental model behind the SDK
- EVM → Overview — the full EVM surface (
readContract,multicall, NFTs, fees, EIP-712) - UTXO → Overview — PSBT signing, multi-recipient sends
- React Components → Provider — deeper config (themes, DNS override, custom domains)
- Developer Dashboard — configure your app (domains, branding, supported networks)