The Openloop SDK supports remote device access over WalletConnect v2 in two ways.
| Approach | Level | Description |
|---|---|---|
| WcTransport | Low-level | Relays APDU commands through the WalletConnect relay |
| AppKit | High-level | Standard multi-chain WC connection via Reown AppKit |
WcTransport implements ITransport and sends
APDU commands to a remote Openloop Connect app through the WalletConnect
v2 relay.
Hex-encoded APDUs are sent and received over the custom JSON-RPC
method openloop_exchange.
import { WcTransport, EthereumApp, DEFAULT_ETH_PATH } from '@openloop/sdk-core'
import SignClient from '@walletconnect/sign-client'
// Initialize the WalletConnect SignClient
const client = await SignClient.init({
projectId: 'your-project-id',
})
// Pair and establish a session
const { uri, approval } = await client.connect({
requiredNamespaces: {
eip155: {
methods: ['openloop_exchange', 'openloop_lock', 'openloop_unlock'],
chains: ['eip155:1'],
events: [],
},
},
})
// Show the URI as a QR code → the user scans it with Openloop Connect
console.log('Pairing URI:', uri)
// Wait for session approval
const session = await approval()
// Create the WcTransport
const transport = new WcTransport({
client,
session: { topic: session.topic },
chainId: 'eip155:1', // optional (default: 'eip155:1')
})
await transport.open()
// The regular App classes work as-is
const eth = new EthereumApp(transport)
const { address } = await eth.getAddress(DEFAULT_ETH_PATH)interface WcTransportOptions {
client: IWcSignClient // WalletConnect SignClient instance
session: WcSession // Active session { topic: string }
chainId?: string // Chain ID (default: "eip155:1")
}
interface IWcSignClient {
request<T>(params: {
topic: string
chainId: string
request: { method: string; params: unknown }
}): Promise<T>
}
interface WcSession {
topic: string
}| Method | Description |
|---|---|
openloop_exchange |
Send and receive APDU commands |
openloop_lock |
Acquire the exclusive device lock |
openloop_unlock |
Release the exclusive device lock |
ITransport interface, so every App class
works as-islock / unlock) also works
over the relayWith Reown AppKit you sign using WalletConnect’s standard methods —
eth_signTransaction, personal_sign, and so on.
This approach never handles APDUs directly; Openloop Connect drives the
device internally.
Chains: - eip155:1 — Ethereum Mainnet -
eip155:11155111 — Sepolia Testnet
Methods: | Method | Description | |———|——| |
eth_sendTransaction | Send a transaction | |
eth_signTransaction | Sign a transaction | |
eth_sign | Sign data | | personal_sign | Sign
an EIP-191 message | | eth_signTypedData | Sign EIP-712
typed data | | eth_signTypedData_v3 | EIP-712 v3 | |
eth_signTypedData_v4 | EIP-712 v4 | |
wallet_getCapabilities | Get the wallet’s capabilities
|
Chains: - bip122:000000000019d6689c085ae165831e93 —
Bitcoin Mainnet - bip122:000000000933ea01ad0ee984209779ba —
Bitcoin Testnet3
Methods: | Method | Description | |———|——| | signPsbt |
Sign a PSBT | | getAccountAddresses | Get the account
addresses | | signMessage | Sign a message | |
sendTransfer | Send a transfer |
Chains: - solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp —
Mainnet Beta - solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1 —
Devnet
Methods: | Method | Description | |———|——| |
solana_signTransaction | Sign a transaction | |
solana_signAllTransactions | Sign multiple transactions | |
solana_signAndSendTransaction | Sign and send | |
solana_signMessage | Sign a message |
Chains: - tron:0x2b6653dc — TRON Mainnet -
tron:0x94a9059e — TRON Shasta Testnet
Methods: | Method | Description | |———|——| |
tron_signTransaction | Sign a transaction | |
tron_signMessage | Sign a message |
import { createAppKit } from '@reown/appkit/react'
import { WagmiAdapter } from '@reown/appkit-adapter-wagmi'
import { SolanaAdapter } from '@reown/appkit-adapter-solana'
import { BitcoinAdapter } from '@reown/appkit-adapter-bitcoin'
import { TronAdapter } from '@reown/appkit-adapter-tron'
import { mainnet, sepolia } from '@reown/appkit/networks'
const projectId = 'your-walletconnect-project-id'
// Create the adapters
const wagmiAdapter = new WagmiAdapter({ projectId, networks: [mainnet, sepolia] })
const solanaAdapter = new SolanaAdapter()
const bitcoinAdapter = new BitcoinAdapter()
const tronAdapter = new TronAdapter()
// Create the AppKit instance
const modal = createAppKit({
projectId,
adapters: [wagmiAdapter, solanaAdapter, bitcoinAdapter, tronAdapter],
networks: [mainnet, sepolia],
metadata: {
name: 'My dApp',
description: 'My dApp with Openloop',
url: 'https://my-dapp.com',
icons: ['https://my-dapp.com/icon.png'],
},
customWallets: [
{
id: 'openloop-connect',
name: 'Openloop Connect',
homepage: 'https://crypto.haudi.jp/openloop/',
mobile_link: 'openloop://',
desktop_link: 'openloop://',
image_url: 'https://crypto.haudi.jp/openloop/images/logo/openloop-icon.png',
},
],
})| Criterion | WcTransport | AppKit |
|---|---|---|
| APDU-level control | ✓ Full control | ✗ Abstracted away |
| Ease of adoption | Somewhat involved | Easy |
| UI components | None (build your own) | Modal included |
| Multi-chain | Manual switching | Automatic switching |
| EIP-7702 | ✓ Supported | ✗ Not supported |
| Supported wallets | Openloop Connect only | Every WC-compatible wallet |
Recommendation: - Integrating with the Openloop device is the main goal → WcTransport - You want to support multiple wallets → AppKit
exchange timeout of WcTransport
depends on the constraints of the WalletConnect relay