本文へ移動

署名

ウォレットセッションで、NEARトランザクション、NEP-413メッセージ、EVM系トランザクションに署名します。

署名 ​

署名呼び出しは、認証済みウォレットを自動的に特定します。 アプリが複数のウォレットやチェーンを扱う場合は、対象を明示してください。 以下の実装例は進行状況のイベントを通知するため、署名処理の状態をUIに表示できます。

前提条件 ​

設定済みのSeamsWebProviderと、サインイン済みのウォレットが必要です。 まだロック中の場合は、ウォレットの設定と認証を参照してください。

署名に個別のアンロック手順は必要ありません。 各リクエストでウォレットの確認画面が開き、ユーザーが取引を承認できます。 連続する署名のたびに確認を求めない製品では、unlockで署名セッションを用意します。

ts
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トランザクションを送信する ​

この実装例は、NEARテストネットのアカウントにset_greetingの関数呼び出しを送り、EXECUTED_OPTIMISTICを待ちます。

tsx
import {
  functionCall,
  logWalletEvents,
  TxExecutionStatus,
  useSeams,
  type BoundNearSigner,
} from '@seams/wallet/react';

async function signGreeting(near: BoundNearSigner): Promise<void> {
  await near.signAndSendTransaction({
    receiverId: 'guest-book.testnet',
    actions: [functionCall({ method: 'set_greeting', args: { greeting: 'Hello from Seams' } })],
    options: {
      waitUntil: TxExecutionStatus.EXECUTED_OPTIMISTIC,
      onEvent: logWalletEvents(),
    },
  });
}

export function SetGreetingButton() {
  const { wallet } = useSeams();
  if (wallet?.access !== 'wallet' || !wallet.near) return null;

  // Each request opens wallet confirmation for this transaction.
  return <button onClick={signGreeting.bind(null, wallet.near)}>Sign transaction</button>;
}

ボタンの処理は、設定した実行状態に達してから完了します。 useSeams()は、サインアウト中にwallet: nullを返し、NEARアカウントの準備完了までwallet.near: nullを返します。 ボタンの前の型ガードにより、そのアカウントがない状態での署名を防ぎます。

NEP-413メッセージに署名する ​

購入の承認など、ドメインに紐づくオフチェーンのメッセージにはNEP-413を使います。

ts
import { logWalletEvents, type SeamsWeb } from '@seams/wallet';

export async function signCheckoutMessage(seams: SeamsWeb) {
  const result = await seams.near.signMessage({
    // Omitting the subject targets the authenticated wallet and its NEAR
    // account; pass `walletSession` (a wallet id is enough) or `nearAccount` to
    // name an exact one.
    params: {
      message: 'Approve checkout quote #quote_123',
      recipient: 'merchant.example',
      state: 'quote_123',
    },
    options: { onEvent: logWalletEvents() },
  });

  if (!result.success) {
    throw new Error(result.error);
  }
  return result;
}

成功結果には、署名済みメッセージのデータが含まれます。 このヘルパーは、SDKがsuccess: falseを返した後だけ例外を送出します。 必要に応じて、呼び出し側で例外を画面内のエラー状態へ置き換えてください。

EVM系トランザクションを実行する ​

型付きのEIP-1559リクエストを作成し、対応するネットワークのチェーン対象を指定します。

既定のseamsTestnetConfigは、NEARとTempoを選択します。 Ethereum Sepoliaの実装例を実行するには、プロバイダーを作成する前に明示的に設定してください。

ts
import { defineSeamsConfig } from '@seams/wallet/react';

const config = defineSeamsConfig({
  walletOrigin: import.meta.env.VITE_WALLET_ORIGIN,
  relayerUrl: import.meta.env.VITE_RELAYER_URL,
  publishableKey: import.meta.env.VITE_SEAMS_PUBLISHABLE_KEY,
  chains: [{ network: 'near-testnet' }, { network: 'ethereum-sepolia', chainId: 11_155_111 }],
});
ts
import { logWalletEvents, parseEther, type SeamsWeb } from '@seams/wallet';

// EIP-1559 on any configured EVM chain. `seams.tempo` mirrors this API for
// Tempo's EIP-2718 typed transactions; the two stay separate because the
// envelopes and the signed results differ.
export async function executeEvmTransaction(seams: SeamsWeb): Promise<string> {
  const execution = await seams.evm.signAndSendTransaction({
    // A configured network slug. The RPC endpoint comes from that chain, and
    // `tx.chainId` is filled in from it. Omitting `walletSession` targets the
    // authenticated wallet.
    chainTarget: 'ethereum-sepolia',
    request: {
      chain: 'evm',
      kind: 'eip1559',
      senderSignatureAlgorithm: 'secp256k1',
      tx: {
        maxPriorityFeePerGas: 1n,
        maxFeePerGas: 1n,
        gasLimit: 21_000n,
        to: '0x1234567890abcdef1234567890abcdef12345678',
        value: parseEther('0'),
        data: '0x',
      },
    },
    options: { onEvent: logWalletEvents() },
  });
  console.log('transaction hash', execution.txHash);
  return execution.txHash;
}

この実装例はEthereum Sepoliaを対象とし、トランザクションハッシュを返します。 実際に送信する前に、宛先、手数料、チェーン対象をアプリのトランザクションビルダーの値へ置き換えてください。 tx.chainIdとRPCエンドポイントは、どちらもチェーン対象から取得するため、呼び出し時に再指定しません。

TempoのEIP-2718型付きトランザクションには、同じメソッド構成のseams.tempoを使います。

期待する結果 ​

  • NEAR署名は、指定したトランザクション実行状態に達してから完了します。
  • NEP-413は、署名済みメッセージの成功結果を返します。
  • EVM系の実行は、トランザクション送信後にtxHashを返します。

進行状況のコールバックは、SigningFlowEventを受け取ります。 状態表示に使い、照合用に最終的な操作識別情報を保持してください。

対処可能な失敗 ​

  • 承認のキャンセルやポリシーによる拒否は、現在のリクエストを終了します。操作意図の下書きを保持し、再確認と再試行を可能にしてください。
  • ウォレットセッションが期限切れか利用枠を使い切った場合は、再試行の前に再度アンロックしてください。
  • 署名やRPCの失敗は、試行中の操作に紐づけてください。最初のハッシュまたはノンスの状態を照合してから、2つ目の取引を送信してください。
  • SDKを呼ぶ前に、アプリ側でチェーンID、宛先、手数料、アカウント参照を検証してください。

次に、ウォレットの高度な操作、イベントと進行状況、結果とエラーを参照してください。