keepkey.js
A library for interacting with a KeepKey hardware wallet over WebUSB.
WebUSB requires https
to work, try the library out here! https://example-ldjlfnposh.now.sh
Installation
yarn add @keepkey/keepkey.js
Importing library
You can import the generated bundle to use the whole library generated by this starter:
import { KeepKeyManager, KeepKey, WebUSBDevice, Messages, Types, messageTypeRegistry, ... } from '@keepkey/keepkey.js'
Additionally, you can import the transpiled modules from dist/lib
in case you have a modular library:
import KeepKey from '@keepkey/keepkey.js/dist/lib/keepkey'
import WebUSBDevice from '@keepkey/keepkey.js/dist/lib/webUSBDevice'
Usage
The recommended way to use the library is with the KeepKeyManager interface, which will handle initializing, getting, and removing KeepKeys. WebUSB is Chrome only.
import { KeepKeyManager, WebUSBDevice } from '@keepkey/keepkey.js'
await WebUSBDevice.requestPair()
const keepkeyManager = new KeepKeyManager({
onConnectCallback: (deviceID) => console.log('device was connected!'),
onDisconnectCallback: (deviceID) => console.log('device was disconnected!')
})
await keepkeyManager.initializeWebUSBDevices()
const pingsByDeviceID = keepkeyManager.exec('ping', { message: 'TEST' })
const keepkey = manager.get()
const keepkey = manager.get("YOUR-DEVICE-ID")
const { message } = await keepkey.ping({ message: 'TEST' })
keepkey.ping({ message: 'TEST' }).then(response => console.log(response.message))
You can also use a device instance to roll your own messaging
import { WebUSBDevice, Messages } from '@keepkey/keepkey.js'
const usbDevice = await WebUSBDevice.requestPair()
const device = new WebUSBDevice({ usbDevice })
const ping = new Messages.Ping()
ping.setMessage('TEST')
const [responseTypeEnum, responseMessage] = await device.exchange(
Messages.MessaageType.MESSAGETYPE_PING,
ping
)
console.log(responseMessage.toObject().message)
Handling Prompt Events
The KeepKeyManager
exposes an event emitter - keepkeyManager.deviceEvents
that decorates all events emitted by keepkey.device.events
with the ID of the device that emitted it.
Both keepkeyManager.deviceEvents
and keepkey.device.events
are instances of eventemitter2 because the KeepKeyManager
can emit events for all initialized devices.
Devices that have already been paired, and get connected during your app's runtime will automatically be added to the KeepKeyManager
and start emitting events.
import { KeepKeyManager, Messages } from '@keepkey/keepkey.js'
const { MessageType: { MESSAGETYPE_FAILURE } } = Messages
const keepkeyManager = new KeepKeyManager()
await keepkeyManager.initializeWebUSBDevices()
keepkeyManager.deviceEvents.on([String(MESSAGETYPE_FAILURE), '*'], (deviceID, ...args) => {
console.log(`${deviceID} emitted an error. UH OH`)
})
keepkeyManager.deviceEvents.on([String(MESSAGETYPE_FAILURE), '5B58BDA6CE3B9404BA6B660D'], (deviceID, ...args) => {
console.log(`5B58BDA6CE3B9404BA6B660D emitted an error. UH OH`)
})
keepkeyManager.get('5B58BDA6CE3B9404BA6B660D').device.events.on(String(MESSAGETYPE_FAILURE), (deviceID, ...args) => {
console.log(`I emitted an error. UH OH`)
})
Event Types
Here are the prompt events that get emitted in response to various prompts:
import { KeepKeyManager, Messages } from '@keepkey/keepkey.js'
const { MessageType: {
MESSAGETYPE_PINMATRIXACK,
MESSAGETYPE_FAILURE,
MESSAGETYPE_BUTTONREQUEST,
MESSAGETYPE_PINMATRIXREQUEST,
MESSAGETYPE_PASSPHRASEREQUEST,
MESSAGETYPE_CHARACTERREQUEST
}} = Messages
When the device prompts a user for a button press, or pin, the device will emit an event on its k.device.events
event emitter.
import { KeepKey, Messages } from '@keepkey/keepkey.js'
const { MessageType } = Messages
const {
MESSAGETYPE_BUTTONREQUEST,
MESSAGETYPE_PINMATRIXREQUEST,
MESSAGETYPE_PASSPHRASEREQUEST
} = MessageType
const keepkey = KeepKey.withWebUSB()
keepkey.device.events.on(String(MESSAGETYPE_BUTTONREQUEST), (event) => {
...
})
keepkey.changePin()
Recovering a device with a seed phrase
Recovering with seed words using a wiped device.
window.k.recoveryDevice({
wordCount: 12,
passphraseProtection: false,
pinProtection: false,
label: "Testy McTestFace"
})
At this point, KeepKey will display a one-time-pad alphebet cipher.
Using the cipher displayed on KeepKey's screen to translate characters,
input the seed word character sequence. Note that the cipher will mutate
after each character is input.
window.k.acknowledgeWithCharacter("X")
When KeepKey displays a checkmark next to a complete word that you are entering,
you move to the next word input by sending KeepKey a space character.
window.k.acknowledgeWithCharacter(" ")
Two additional methods facilitate deletion and completion of the
recovery sequence.
window.k.acknowledgeWithCharacterDelete()
window.k.acknowledgeWithCharacterDone()
Building
First, run yarn
to get dependencies.
Run yarn make:protos
to compile KeepKey proto files into src/proto.json
and src/kkProto
code and type definitions.
Run yarn build
to create umd
, commonjs
, and browser
bundles in the /dist
direcory.
Run yarn make:example
to build, and copy browser bundles to the /example
static site. You can then deploy the the example
directory to a webserver with https
so WebUSB
works. For example: yarn make:example && now example
Updating device-protocol
proto's
Grab the desired commit hash from github.com/KeepKey/device-protocol and update it in package.json
a la:
{
"dependencies": {
"device-protocol": "git+https://git@github.com/KeepKey/device-protocol.git#4ee29339fb8a9c916bcba9079aebd5254a16df08",
}
}
then run yarn make:protos
to compile new code from the updated protos.
Developing
When developing with WebUSB devices, you'll need an https
connection, and configure your browser to allow insecure localhost
To compile and watch the browser bundle, run yarn dev:example
Then, cd example
, and python server.py
to run a server with ssl that runs on localhost:8888
`