Openloop SDK を使ってハードウェアウォレットに接続し、アドレス取得やトランザクション署名を行う手順を説明します。
コアパッケージと、使用する接続方式の Transport パッケージをインストールします。
# USB 接続(Chrome/Edge デスクトップ)
npm install @openloop/sdk-core @openloop/transport-webhid
# Bluetooth 接続(Chrome/Edge + Android)
npm install @openloop/sdk-core @openloop/transport-webble
# 全ブラウザ対応(Openloop Connect 経由)
npm install @openloop/sdk-core @openloop/transport-local
# iOS Safari
npm install @openloop/sdk-core @openloop/transport-safari
# Node.js / Electron
npm install @openloop/sdk-core @openloop/transport-usbすべての接続方式で共通の 4 ステップです。
1. Transport 接続 → 2. App 作成 → 3. 操作 → 4. 切断
import { EthereumApp, DEFAULT_ETH_PATH } from '@openloop/sdk-core'
import { WebHidTransport } from '@openloop/transport-webhid'
// 1. Transport 接続(ユーザージェスチャー内で呼び出す)
const transport = await WebHidTransport.connect()
// 2. App 作成
const eth = new EthereumApp(transport)
// 3. 操作
const { address } = await eth.getAddress(DEFAULT_ETH_PATH)
console.log('Address:', address)
// 4. 切断
await transport.close()注意:
WebHidTransport.connect()とWebBleTransport.connect()はブラウザのセキュリティ制約により、ボタンクリック等のユーザージェスチャー内で呼び出す必要があります。
import {
EthereumApp,
DEFAULT_ETH_PATH,
type SignatureResult,
} from '@openloop/sdk-core'
import { WebHidTransport } from '@openloop/transport-webhid'
async function main() {
// 接続
const transport = await WebHidTransport.connect()
const eth = new EthereumApp(transport)
try {
// アドレス取得
const { address, publicKey } = await eth.getAddress(DEFAULT_ETH_PATH)
console.log('Address:', address)
console.log('Public Key:', publicKey)
// メッセージ署名(EIP-191 personal_sign)
const msgSig: SignatureResult = await eth.signPersonalMessage(
DEFAULT_ETH_PATH,
'Hello, Openloop!',
1 // chainId: Ethereum Mainnet
)
console.log('Message Signature:', { v: msgSig.v, r: msgSig.r, s: msgSig.s })
// トランザクション署名(RLP エンコード済みの raw tx を渡す)
const rawTx = 'f86c...' // RLP-encoded transaction hex
const txSig: SignatureResult = await eth.signTransaction(DEFAULT_ETH_PATH, rawTx)
console.log('TX Signature:', { v: txSig.v, r: txSig.r, s: txSig.s })
// EIP-712 Typed Data 署名
const typedSig: SignatureResult = await eth.signTypedData(
DEFAULT_ETH_PATH,
'aabbccdd...', // domainSeparatorHash (32 bytes hex)
'11223344...', // messageHash (32 bytes hex)
1
)
console.log('TypedData Signature:', typedSig)
} finally {
await transport.close()
}
}import {
BitcoinApp,
BTC_MAINNET_PATH,
type BtcMessageSignature,
} from '@openloop/sdk-core'
import { WebHidTransport } from '@openloop/transport-webhid'
async function main() {
const transport = await WebHidTransport.connect()
const btc = new BitcoinApp(transport)
try {
// アドレス取得(SegWit bech32)
const { address, publicKey, chainCode } = await btc.getAddress(false) // mainnet
console.log('Address:', address) // bc1q...
// メッセージ署名(BIP-137)
const msgSig: BtcMessageSignature = await btc.signMessage(
'Hello, Bitcoin!',
BTC_MAINNET_PATH
)
console.log('Signature (base64):', msgSig.signature)
// PSBT 署名
const psbtHex = '70736274ff...' // PSBT binary as hex
const signedPsbtHex = await btc.signPsbtHex(psbtHex)
console.log('Signed PSBT:', signedPsbtHex)
// PSBT 署名(入力パス指定)
const signedWithPaths = await btc.signPsbtWithPaths(psbtHex, [
{ index: 0, path: "84'/0'/0'/0/0" },
{ index: 1, path: "84'/0'/0'/0/1" },
])
console.log('Signed PSBT with paths:', signedWithPaths)
} finally {
await transport.close()
}
}WebHID を WebBLE に置き換えるだけで、他のコードは同じです。
import { WebBleTransport } from '@openloop/transport-webble'
// Bluetooth 接続(ユーザージェスチャー内で呼び出す)
const transport = await WebBleTransport.connect()
// 以降は WebHID と同じ
const eth = new EthereumApp(transport)
const { address } = await eth.getAddress(DEFAULT_ETH_PATH)Openloop Connect デスクトップアプリ経由で接続します。Firefox や Safari でも動作します。
import { LocalWsTransport } from '@openloop/transport-local'
// Connect アプリが起動しているか確認
const available = await LocalWsTransport.isAvailable()
if (!available) {
console.log('Openloop Connect を起動してください')
}
// 接続
const transport = new LocalWsTransport()
await transport.open()
// 以降は同じ
const eth = new EthereumApp(transport)import { useState, useCallback } from 'react'
import { EthereumApp, DEFAULT_ETH_PATH, type ITransport } from '@openloop/sdk-core'
import { WebHidTransport } from '@openloop/transport-webhid'
function WalletButton() {
const [address, setAddress] = useState<string>('')
const [transport, setTransport] = useState<ITransport | null>(null)
const connect = useCallback(async () => {
const t = await WebHidTransport.connect()
setTransport(t)
const eth = new EthereumApp(t)
const { address } = await eth.getAddress(DEFAULT_ETH_PATH)
setAddress(address)
}, [])
const disconnect = useCallback(async () => {
await transport?.close()
setTransport(null)
setAddress('')
}, [transport])
return (
<div>
{address ? (
<>
<p>{address}</p>
<button onClick={disconnect}>Disconnect</button>
</>
) : (
<button onClick={connect}>Connect Wallet</button>
)}
</div>
)
}サンプルアプリの useOpenloop フック(packages/sample-app/src/hooks/useOpenloop.ts)は、全
Transport 切り替え・WalletConnect 統合・自動再接続を含む実装例です。
WebHID と WebBLE は、以前接続が許可されたデバイスへの自動再接続をサポートしています。
// 以前許可されたデバイスに自動再接続(ユーザージェスチャー不要)
const transport = await WebHidTransport.reconnect()
if (transport) {
// 再接続成功
const eth = new EthereumApp(transport)
} else {
// デバイスが見つからない → connect() でペアリングが必要
}