Appearance
Advanced wallet operations
These operations change access or disclose key material. Start each one from a fresh user action, keep progress visible, and retain the returned identity or receipt for your audit trail.
Prerequisites
Complete wallet setup and authentication and obtain an active wallet session. Device linking also needs a camera or a way to deliver the QR payload between devices.
Link another device
Device 2 starts a short-lived session and displays its QR code. Device 1 scans that code and approves the request with fresh authentication.
tsx
import { useState } from 'react';
import { QRScanMode, ShowQRCode, useDeviceLinking } from '@seams/wallet/react';
import type { LinkDeviceFlowEvent, QrLinkedDeviceSessionPayloadV5 } from '@seams/wallet';
function logLinkEvent(event: LinkDeviceFlowEvent): void {
console.log(event.phase, event.status, event.message);
}
// Device 2: `ShowQRCode` runs the whole start/display/expire cycle, including
// picking the target factor and cancelling an abandoned session.
export function NewDeviceLinkCode() {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<button onClick={() => setIsOpen(true)}>Show link code</button>
<ShowQRCode
isOpen={isOpen}
onClose={() => setIsOpen(false)}
onEvent={logLinkEvent}
onError={(error) => console.error('Device link failed', error)}
/>
</>
);
}
// Device 1: scan the code and approve.
export function ApproveLinkedDevice(props: { qrData: QrLinkedDeviceSessionPayloadV5 }) {
const { linkDevice } = useDeviceLinking({
onEvent: logLinkEvent,
onError: (error) => console.error('Device link failed', error),
});
return (
<button onClick={() => void linkDevice(props.qrData, QRScanMode.CAMERA)}>Approve device</button>
);
}ShowQRCode owns the Device 2 cycle end to end — starting the session, displaying the code, and cancelling an abandoned one on close. Expire abandoned QR sessions and show the device name after success so it can be recognized and revoked later.
Recover a wallet account
Call recovery synchronization with the wallet id from the account record.
ts
import type { SeamsWeb } from '@seams/wallet';
type SyncAccountResult = Awaited<ReturnType<SeamsWeb['recovery']['syncAccount']>>;
export async function recoverWalletAccount(
seams: SeamsWeb,
walletId: string,
): Promise<SyncAccountResult> {
const result = await seams.recovery.syncAccount({ walletId });
if (!result.success) {
throw new Error(result.error);
}
console.log('wallet account restored', result.walletId, result.nearAccountId);
return result;
}The successful result exposes the restored wallet id and NEAR account id. Use the returned values to refresh app state before rendering signing controls.
Export an Ed25519 or ECDSA key
exportKeypair resolves the exact export lane and opens the wallet-origin export viewer in one call.
ts
import { logWalletEvents, type SeamsWeb } from '@seams/wallet';
export async function exportNearKey(seams: SeamsWeb): Promise<void> {
const outcome = await seams.keys.exportKeypair({
kind: 'ed25519',
options: { onEvent: logWalletEvents() },
});
if (outcome.kind === 'relink_required') {
// This device has no canonical owner binding: send the person through
// device linking rather than showing a generic error.
console.warn('Link this device again before exporting:', outcome.reason);
}
}
export async function exportEvmKey(seams: SeamsWeb): Promise<void> {
const outcome = await seams.keys.exportKeypair({
kind: 'ecdsa',
chainTarget: 'tempo-testnet',
options: { onEvent: logWalletEvents() },
});
if (outcome.kind === 'relink_required') {
console.warn('Link this device again before exporting:', outcome.reason);
}
}exportNearKey exports the Ed25519 lane for the wallet's NEAR account; exportEvmKey exports the ECDSA lane for a configured chain. Both default to the authenticated wallet and receive progress through KeyExportFlowEvent.
Check the outcome: relink_required means this device has no canonical owner binding, so route the person through device linking rather than showing a generic error. resolveExactKeyExportLane and exportKeypairWithUI remain available when you want to check export availability before opening the viewer.
Expected result
- Device linking creates a separate device credential and lane.
- Recovery synchronization returns the restored wallet and NEAR identity.
- Export opens a protected viewer after the exact lane is authorized.
Recoverable failures
- A cancelled or expired QR session must be started again. Do not approve an old QR payload.
- Recovery can return a failure result. Keep the existing account state until synchronization succeeds.
- Export can return
relink_requiredinstead of opening the viewer. That is a handled outcome, not an error: send the person through device linking. - Export authorization and viewer errors should end the current disclosure attempt. Ask for fresh authentication before another export.
Read next: linked devices, recovery, export, and rotation, or results and errors.