ウォレットの設定と認証
ホスト環境での登録とサインインの準備
登録画面を開いたら、SeamsWebProvider内にHostedRegistrationを表示してください。 その画面が開いている間に、ウォレットとチャレンジを準備します。結果はアプリ側で処理してください。
SignInIntentButtonは、ポインター操作、フォーカス、タッチを契機にサインイン用コードをダウンロードします。 認証メニューを表示するために、onOpenを指定してください。 ダウンロードだけでは、ウォレットの予約やパスキーのチャレンジの準備は行いません。 registerWallet()を直接呼ぶ場合は、その呼び出しの中で準備を開始します。 ホスト環境への導入を参照してください。
import {
HostedSeamsAuthMenu,
useSeams,
type HostedAuthMenuOutcome,
} from '@seams/wallet/react';
export function HostedRegistration({ onOutcome }: {
onOutcome: (outcome: HostedAuthMenuOutcome) => void;
}) {
return <HostedSeamsAuthMenu initialMode="register" onOutcome={onOutcome} />;
}
export function SignInIntentButton({ onOpen }: { onOpen: () => void }) {
const { seams } = useSeams();
return (
<button
onPointerEnter={seams.authMenu.prepare}
onFocus={seams.authMenu.prepare}
onTouchStart={seams.authMenu.prepare}
onClick={onOpen}
>
Sign in
</button>
);
}新しいアプリにSeamsを追加する際は、この実装例を使ってください。 プロバイダーを一度設定し、ウォレットを登録します。署名前にウォレットセッションを作成してください。
前提条件
@seams/walletをインストールし、設定したウォレットオリジンを配信してください。 設定例にある登録用とリレーヤー用の環境変数を指定してください。
プロバイダーを設定する
useSeamsを呼ぶコンポーネントより上に、SeamsWebProviderを配置します。
import { SeamsWebProvider, seamsTestnetConfig, useSeams } from '@seams/wallet/react';
// Everything else — wallet service path, SDK base path, relayer account, chain
// RPC and explorer URLs — comes from the SDK defaults.
const seamsConfig = seamsTestnetConfig({
walletOrigin: import.meta.env.VITE_WALLET_ORIGIN,
relayerUrl: import.meta.env.VITE_RELAYER_URL,
publishableKey: import.meta.env.VITE_SEAMS_PUBLISHABLE_KEY,
});
function WalletApp() {
const { loginState } = useSeams();
return <p>{loginState.isLoggedIn ? 'Wallet unlocked' : 'Wallet locked'}</p>;
}
export function App() {
return (
<SeamsWebProvider config={seamsConfig}>
<WalletApp />
</SeamsWebProvider>
);
}seamsTestnetConfigには、ウォレットオリジン、リレーヤーURL、公開可能キーが必要です。 ウォレットサービスのパス、SDKのベースパス、リレーヤーアカウント、チェーンのRPC・エクスプローラーURLには既定値を設定します。 この実装例は、アプリ環境からVITE_WALLET_ORIGIN、VITE_RELAYER_URL、VITE_SEAMS_PUBLISHABLE_KEYを読み取ります。 デプロイ先ごとに、自社の値を指定してください。テストネット以外のネットワークにはdefineSeamsConfigを使います。
パスキーで登録する
CreateWalletButtonを表示するか、独自の登録画面からseams.registration.registerWalletを呼び出します。
import type { RegistrationFlowEvent, SeamsWeb } from '@seams/wallet';
import { useSeams, type SeamsContextType } from '@seams/wallet/react';
function logRegistrationEvent(event: RegistrationFlowEvent): void {
console.log(event.phase, event.status, event.message);
}
function assertNever(value: never): never {
throw new Error(`Unexpected NEAR readiness result: ${JSON.stringify(value)}`);
}
async function createWallet(
registerWallet: SeamsContextType['registerWallet'],
seams: SeamsWeb,
registration: Parameters<SeamsContextType['registerWallet']>[0],
): Promise<void> {
try {
const result = await registerWallet({
...registration,
options: { ...registration.options, onEvent: logRegistrationEvent },
});
if (!result.success) {
console.error('Registration failed:', result.error);
return;
}
// `walletId` is the stable identifier for every later wallet operation.
console.log(`Wallet ${result.walletId} registered (${result.kind})`);
if (
result.kind === 'ecdsa_wallet_registered_near_pending' ||
result.kind === 'near_wallet_registered_pending'
) {
const near = await seams.registration.waitForNearAccount({ walletId: result.walletId });
switch (near.kind) {
case 'near_ready':
console.log('NEAR account ready:', near.nearAccountId);
break;
case 'near_failed_retryable':
console.warn('NEAR provisioning needs a retry:', near.reason);
break;
case 'timed_out':
console.warn('NEAR is still pending. Keep this wallet id and check readiness again.');
break;
default:
assertNever(near);
}
}
} catch (error) {
console.error('Wallet creation interrupted:', error);
}
}
export function CreateWalletButton({ registration }: {
registration: Parameters<SeamsContextType['registerWallet']>[0];
}) {
const { registerWallet, seams } = useSeams();
return (
<button onClick={createWallet.bind(null, registerWallet, seams, registration)}>
Create wallet
</button>
);
}RegistrationResultは型付きのユニオンです。 登録が成功すると、すぐに利用可能な状態か、NEARのプロビジョニング待ちの状態を返します。 チェーン固有の機能を参照する前に、結果の分岐を確認してください。 待機中の分岐では、seams.registration.waitForNearAccount({ walletId })を待ってください。 getNearAccountStateを独自にポーリングする必要はありません。
既存のウォレットをアンロックする
アプリのアカウントレコードにあるウォレットIDを、unlockWalletへ渡します。
import type { UnlockFlowEvent } from '@seams/wallet';
import type { SeamsContextType } from '@seams/wallet/react';
export async function unlockWallet(unlock: SeamsContextType['unlock'], walletId: string) {
const result = await unlock(walletId, {
onEvent: (event: UnlockFlowEvent) => console.log(event.phase, event.status, event.message),
});
if (!result.success) {
throw new Error(result.error);
}
// Read `nearAccountId` only from the NEAR branch.
if (result.kind === 'near_wallet_unlocked') {
console.log('NEAR account ready', result.nearAccountId);
} else {
console.log('EVM-family wallet ready', result.walletId);
}
return result;
}成功すると、再利用可能な署名セッションへのアクセスを得られます。 エクスポートには、操作ごとの新たな承認が必要です。 両方の鍵方式に対応するアプリでは、near_wallet_unlockedとecdsa_wallet_unlockedの両分岐を処理してください。
GoogleとメールOTPで認証する
IDプロバイダーのGoogle IDトークンを使い、独自UIでOTPを入力してもらいます。
import type {
GoogleEmailOtpWalletAuthLoginFlow,
GoogleEmailOtpWalletAuthSubmitSuccess,
SeamsWeb,
} from '@seams/wallet';
export async function startGoogleEmailOtpLogin(
seams: SeamsWeb,
googleIdToken: string,
): Promise<GoogleEmailOtpWalletAuthLoginFlow> {
const started = await seams.auth.startGoogleSignIn({
idToken: googleIdToken,
mode: 'login',
loginTarget: { kind: 'discoverable' },
});
if (!started.ok) {
throw new Error(started.error.message);
}
if (started.value.mode !== 'login') {
await started.value.cancel();
throw new Error('This Google account needs wallet registration');
}
return started.value;
}
export async function submitGoogleEmailOtp(
flow: GoogleEmailOtpWalletAuthLoginFlow,
otpCode: string,
): Promise<GoogleEmailOtpWalletAuthSubmitSuccess> {
const submitted = await flow.submit({ otpCode });
if (!submitted.ok) {
throw new Error(submitted.error.message);
}
return submitted.value;
}startGoogleEmailOtpLoginには、既存ウォレットのログインフローが必要です。 アカウントの登録が必要な場合、このヘルパーはログインフローをキャンセルし、その状態を通知します。 アプリは、先に登録へ案内できます。
期待する結果
登録は、ウォレットIDと、利用可能または準備待ちの機能を返します。 アンロックは、利用可能なチェーン識別情報を含むウォレットセッションの結果を返します。 メールOTPは、コードの受理後に認証済みログイン結果を返します。
対処可能な失敗
- パスキーやOTPの入力をキャンセルすると、現在の試行は終了します。同じ画面から再開できるようにしてください。
- 登録やアンロックの失敗結果には、エラー文字列が含まれます。簡潔なメッセージを表示し、結果にウォレットIDがある場合は保持してください。
- Googleのログインフローが登録モードの場合は、登録画面へ進めてください。同じウォレット状態でログインを繰り返さないでください。
- セッションが期限切れか利用枠を使い切った場合は、署名やエクスポートの前に、再度アンロックしてください。
次に、署名、ウォレットの高度な操作、結果とエラーを参照してください。