Openloop SDK は複数の接続方式(Transport)をサポートしています。dApp の対象プラットフォームに応じて適切な Transport を選択してください。
| Transport | Chrome/Edge | Firefox | Safari (Desktop) | iOS Safari | Android Chrome | Node.js / Electron |
|---|---|---|---|---|---|---|
| WebHID (USB) | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ |
| Web Bluetooth | ✓ | ✗ | ✗ | ✗ | ✓ | ✗ |
| LocalWS | ✓ | ✓ | ✓ | ✗ | ✓ | ✗ |
| Safari Extension | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ |
| USB HID (native) | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ |
| WalletConnect | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ |
WalletConnect はデバイスと直接通信するのではなく、Openloop Connect アプリ経由のリモート署名です。デバイス操作(アドレス取得等)は Connect アプリ側で行われます。
パッケージ: @openloop/transport-webhid
対応環境: Chrome 89+ / Edge 89+(デスクトップのみ)
USB ケーブルでデバイスを直接接続します。最も高速で安定した接続方式です。
| メソッド | 説明 |
|---|---|
isSupported(): boolean |
WebHID API が利用可能か |
connect(): Promise<WebHidTransport> |
デバイスピッカーを表示して接続(ユーザージェスチャー必要) |
reconnect(): Promise<WebHidTransport \| null> |
以前許可されたデバイスに自動再接続 |
import { WebHidTransport } from '@openloop/transport-webhid'
// サポート確認
if (!WebHidTransport.isSupported()) {
console.log('WebHID is not supported in this browser')
}
// 初回接続(ボタンクリック内で)
const transport = await WebHidTransport.connect()
// 自動再接続(ページ読み込み時)
const transport = await WebHidTransport.reconnect()
// 切断検知
transport.onDisconnect(() => {
console.log('Device disconnected')
})パッケージ: @openloop/transport-webble
対応環境: Chrome 56+ / Edge
79+(デスクトップ)、Android Chrome
Bluetooth Low Energy (BLE) でワイヤレス接続します。
| メソッド | 説明 |
|---|---|
isSupported(): boolean |
Web Bluetooth API が利用可能か |
connect(): Promise<WebBleTransport> |
デバイスピッカーを表示して接続(ユーザージェスチャー必要) |
reconnect(): Promise<WebBleTransport \| null> |
以前ペアリングしたデバイスに再接続(Chrome 100+) |
import { WebBleTransport } from '@openloop/transport-webble'
if (!WebBleTransport.isSupported()) {
console.log('Web Bluetooth is not supported')
}
// 初回接続
const transport = await WebBleTransport.connect()
// 自動再接続
const transport = await WebBleTransport.reconnect()exchange() 呼び出し時に自動再接続を試みます。パッケージ: @openloop/transport-local
対応環境: WebSocket をサポートする全ブラウザ
前提条件: Openloop Connect
デスクトップアプリが起動していること
Openloop Connect デスクトップアプリが提供する WebSocket
サーバー(ws://127.0.0.1:21320)経由で通信します。Firefox、Safari
等の WebHID/WebBLE 非対応ブラウザでも動作します。
| メソッド | 説明 |
|---|---|
isAvailable(port?, host?): Promise<boolean> |
Connect サーバーが稼働しているか |
import { LocalWsTransport } from '@openloop/transport-local'
// Connect アプリの起動確認
const available = await LocalWsTransport.isAvailable()
// 接続
const transport = new LocalWsTransport({
port: 21320, // デフォルト
host: '127.0.0.1', // デフォルト
})
await transport.open()
// デバイス接続状態の変更通知
transport.onDeviceChange((connected: boolean) => {
console.log('Device:', connected ? 'connected' : 'disconnected')
})JSON-RPC 2.0 over WebSocket:
| メソッド | 説明 |
|---|---|
openloop_exchange |
APDU コマンド送受信 |
openloop_lock |
デバイスの排他ロック取得 |
openloop_unlock |
デバイスの排他ロック解放 |
openloop_status |
デバイス接続状態の取得 |
パッケージ: @openloop/transport-safari
対応環境: iOS Safari(Openloop Safari Extension
インストール済み)
Safari Web Extension を経由して iOS の CoreBluetooth にアクセスし、BLE でデバイスと通信します。
| メソッド | 説明 |
|---|---|
isAvailable(timeout?): Promise<boolean> |
Safari Extension がインストールされているか(デフォルト 3 秒タイムアウト) |
scan(duration?): Promise<DeviceInfo[]> |
BLE デバイスをスキャン |
connect(deviceId?): Promise<SafariTransport> |
デバイスに接続 |
import { SafariTransport } from '@openloop/transport-safari'
// Extension の確認
const available = await SafariTransport.isAvailable()
// デバイスのスキャン
const devices = await SafariTransport.scan(5000) // 5秒スキャン
console.log('Found devices:', devices)
// 接続
const transport = await SafariTransport.connect(devices[0]?.deviceId)パッケージ: @openloop/transport-usb
対応環境: Node.js / Electron 依存:
node-hid
ブラウザを使わずに直接 USB デバイスと通信します。サーバーサイドやデスクトップアプリに適しています。
| メソッド | 説明 |
|---|---|
discover(): UsbDeviceInfo[] |
接続されている Openloop/Ledger デバイスを列挙 |
import { UsbHidTransport } from '@openloop/transport-usb'
// デバイス検出
const devices = UsbHidTransport.discover()
if (devices.length === 0) {
console.log('No device found')
}
// 接続
const transport = new UsbHidTransport({ path: devices[0].path })
await transport.open()
// 自動リトライ(デフォルト最大3回)
const transport = new UsbHidTransport({
path: devices[0].path,
maxRetries: 3,
exchangeTimeout: 120000, // 2分
})パッケージ:
@openloop/sdk-core(WcTransport は sdk-core に含まれる)
対応環境: WebSocket をサポートする全ブラウザ
WalletConnect v2 リレー経由で、リモートの Openloop Connect
アプリと通信します。カスタムメソッド openloop_exchange
を使って APDU コマンドを中継します。
詳細は WalletConnect 連携 を参照してください。
OpenloopSDK クラスを使うと、利用可能な Transport
を自動検出して接続できます。
import { OpenloopSDK, EthereumApp, DEFAULT_ETH_PATH } from '@openloop/sdk-core'
import { WebHidTransport } from '@openloop/transport-webhid'
import { WebBleTransport } from '@openloop/transport-webble'
import { LocalWsTransport } from '@openloop/transport-local'
// Transport を登録
OpenloopSDK.registerTransport('webhid', {
factory: () => WebHidTransport.connect(),
name: 'WebHID (USB)',
isAvailable: () => WebHidTransport.isSupported(),
})
OpenloopSDK.registerTransport('webble', {
factory: () => WebBleTransport.connect(),
name: 'Web Bluetooth',
isAvailable: () => WebBleTransport.isSupported(),
})
OpenloopSDK.registerTransport('local', {
factory: async () => {
const t = new LocalWsTransport()
await t.open()
return t
},
name: 'LocalWS (Connect)',
isAvailable: () => LocalWsTransport.isAvailable(),
})
// 利用可能な Transport を確認
const transports = await OpenloopSDK.discover()
console.log(transports)
// [
// { type: 'webhid', name: 'WebHID (USB)', available: true },
// { type: 'webble', name: 'Web Bluetooth', available: true },
// { type: 'local', name: 'LocalWS (Connect)', available: false },
// ]
// 指定した Transport で接続
const transport = await OpenloopSDK.connect({ transport: 'webhid' })
// または最初に利用可能な Transport で接続
const transport = await OpenloopSDK.connect()// ページ読み込み時に以前のデバイスへ自動再接続
async function autoReconnect(): Promise<ITransport | null> {
// WebHID: 以前許可されたデバイスを検出
if (WebHidTransport.isSupported()) {
const transport = await WebHidTransport.reconnect()
if (transport) return transport
}
// WebBLE: 以前ペアリングしたデバイスを検出(Chrome 100+)
if (WebBleTransport.isSupported()) {
const transport = await WebBleTransport.reconnect()
if (transport) return transport
}
// LocalWS: Connect アプリが起動していれば接続
if (await LocalWsTransport.isAvailable()) {
const transport = new LocalWsTransport()
await transport.open()
return transport
}
return null
}