Wallet Integration
Guide to integrating WalletPair into your wallet application.
Create a Session
import { WalletSession, WebSocketTransport } from 'walletpair-sdk';
const transport = new WebSocketTransport('wss://relay.walletpair.org/v1');
const session = new WalletSession({
transport,
capabilities: {
methods: [
'wallet_getAccounts',
'wallet_sendTransaction',
'wallet_signMessage',
'wallet_signTypedData',
'wallet_switchChain',
],
events: ['accountsChanged', 'chainChanged'],
chains: ['eip155:1', 'eip155:137'],
},
meta: {
name: 'My Wallet',
description: 'A non-custodial wallet',
url: 'https://wallet.example',
icon: 'https://wallet.example/icon.png',
},
});The capabilities object declares what your wallet supports. Only include methods your
wallet can actually handle. wallet_sendTransaction is optional — cold wallets and
hardware signers that cannot broadcast should omit it.
Join a Pairing
// Parse the QR code / pairing URI
await session.prepareJoin(pairingUri);
// Display session fingerprint to the user for verification
console.log('Session fingerprint:', session.sessionFingerprint);
// Confirm the join (dApp auto-accepts after sealed_join verification)
await session.confirmJoin();The two-step join (prepareJoin + confirmJoin) lets you display the
session fingerprint before completing the handshake. The dApp auto-accepts once it verifies
the sealed join payload.
Handle Requests
session.on('request', async (req) => {
console.log('Method:', req.method);
console.log('Params:', req.params);
switch (req.method) {
case 'wallet_getAccounts':
await session.approve(req.id, {
accounts: [{ address: '0x...', chains: ['eip155:1', 'eip155:137'] }],
});
break;
case 'wallet_signMessage':
// Show confirmation UI to user
const signature = await sign(req.params.message);
await session.approve(req.id, { signature });
break;
default:
await session.reject(req.id, {
code: 'unsupported_method',
message: `Method ${req.method} not supported`,
});
}
});Every request must be either approved or rejected. The wallet must display a confirmation UI for signing and transaction methods — never blind-sign.
Push Events
// Notify the dApp when accounts change
session.pushEvent('accountsChanged', {
accounts: [{ address: '0xNew...', chains: ['eip155:1'] }],
});
// Notify when chain changes
session.pushEvent('chainChanged', {
chain: 'eip155:137',
});Push events proactively to keep the dApp in sync. Always emit chainChanged after a wallet_switchChain approval.
Persistence
const session = new WalletSession({
transport,
capabilities: { /* ... */ },
persistence: {
save: (snapshot) => secureStore.set('walletpair.session', snapshot),
load: () => secureStore.get('walletpair.session'),
clear: () => secureStore.delete('walletpair.session'),
},
});Use a secure storage backend (e.g., Expo SecureStore on mobile, encrypted IndexedDB on web). Session snapshots contain cryptographic keys and must be stored securely.
Security Requirements
- All signing methods must show a user confirmation UI
- Never blind-sign transactions or EIP-712 data
- Warn users that EIP-191 signatures are not chain-bound
- Detect and warn on Permit/spending-allowance patterns in EIP-712
- Verify that
chainparam matchestx.chainIdwhen both are present