Appearance
Add devices, export, and recover
Use these flows after your first wallet can sign. Each operation has its own authorization and result; keep it separate from normal signing.
Link another device
Device 2 starts a link session and displays a QR code. Device 1 scans that code and approves the new device.
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 side end to end: it starts the link session, displays the code, and cancels an abandoned session on close. useDeviceLinking runs the Device 1 side and reports progress through onEvent and failures through onError. Request a fresh code instead of reusing an old QR payload.
Export a key
exportKeypair resolves the exact export lane and opens the wallet-origin export viewer in one call, from a freshly authorized action.
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);
}
}Ed25519 export uses the wallet's NEAR account; ECDSA export names a configured chain. Both default to the signed-in wallet — pass walletSession, nearAccount, or chainTarget to name an exact one.
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. Use resolveExactKeyExportLane and exportKeypairWithUI directly when you want to check export availability before opening the viewer.
Never place the returned key material in logs, URLs, or application analytics.
Recover a wallet account
Synchronize the wallet record when your recovery flow needs to restore its wallet-scoped account identity.
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;
}Check success before reading walletId or nearAccountId. The helper throws for a failed synchronization so the caller can render a retry state.
Delegation and rotation
Delegated agents, lane refresh, and key rotation add policy and deployment choices that depend on your product. Start with delegated agents, linked devices, or recovery, export, and rotation.
Safe retries
- Ask for fresh authorization after cancellation or session expiry.
- Do not retry export or rotation with a stale session.
- Revoke a linked device or delegated lane when it should no longer sign.