2018-05-04 04:00:34 +00:00
|
|
|
import EthTx from 'ethereumjs-tx';
|
|
|
|
import { addHexPrefix, toBuffer } from 'ethereumjs-util';
|
2018-05-04 04:26:25 +00:00
|
|
|
import { WalletLib } from 'shared/enclave/types';
|
2018-04-27 20:49:17 +00:00
|
|
|
import TransportNodeHid from '@ledgerhq/hw-transport-node-hid';
|
|
|
|
import LedgerEth from '@ledgerhq/hw-app-eth';
|
2018-05-04 04:44:17 +00:00
|
|
|
let transport: any;
|
2018-04-27 20:49:17 +00:00
|
|
|
|
|
|
|
async function getEthApp() {
|
2018-05-04 04:00:34 +00:00
|
|
|
try {
|
2018-05-04 04:44:17 +00:00
|
|
|
if (!transport) {
|
|
|
|
transport = await TransportNodeHid.create();
|
|
|
|
transport.on('disconnect', () => (transport = null));
|
|
|
|
}
|
2018-05-04 04:00:34 +00:00
|
|
|
return new LedgerEth(transport);
|
|
|
|
} catch (err) {
|
|
|
|
if (err && err.message && err.message.includes('cannot open device with path')) {
|
|
|
|
throw new Error(
|
2018-05-04 04:44:17 +00:00
|
|
|
'Your Ledger is currently in use with another application. Please wait, or close other wallet applications before trying again.'
|
2018-05-04 04:00:34 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
throw err;
|
|
|
|
}
|
2018-04-27 20:49:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const Ledger: WalletLib = {
|
2018-05-04 04:26:25 +00:00
|
|
|
async getChainCode(dpath) {
|
2018-04-27 20:49:17 +00:00
|
|
|
const app = await getEthApp();
|
|
|
|
try {
|
2018-04-29 21:18:39 +00:00
|
|
|
const res = await app.getAddress(dpath, false, true);
|
2018-04-27 20:49:17 +00:00
|
|
|
return {
|
|
|
|
publicKey: res.publicKey,
|
2018-05-04 20:59:04 +00:00
|
|
|
chainCode: res.chainCode as string
|
2018-04-27 20:49:17 +00:00
|
|
|
};
|
|
|
|
} catch (err) {
|
2018-04-29 21:18:39 +00:00
|
|
|
throw new Error('Failed to connect to Ledger');
|
2018-04-27 20:49:17 +00:00
|
|
|
}
|
2018-05-04 04:00:34 +00:00
|
|
|
},
|
|
|
|
|
2018-05-04 04:26:25 +00:00
|
|
|
async signTransaction(tx, path) {
|
2018-05-04 04:00:34 +00:00
|
|
|
const app = await getEthApp();
|
|
|
|
const ethTx = new EthTx({
|
|
|
|
...tx,
|
|
|
|
v: Buffer.from([tx.chainId]),
|
|
|
|
r: toBuffer(0),
|
|
|
|
s: toBuffer(0)
|
|
|
|
});
|
|
|
|
|
|
|
|
const rsv = await app.signTransaction(path, ethTx.serialize().toString('hex'));
|
|
|
|
const signedTx = new EthTx({
|
|
|
|
...tx,
|
|
|
|
r: addHexPrefix(rsv.r),
|
|
|
|
s: addHexPrefix(rsv.s),
|
|
|
|
v: addHexPrefix(rsv.v)
|
|
|
|
});
|
|
|
|
return {
|
|
|
|
signedTransaction: signedTx.serialize().toString('hex')
|
|
|
|
};
|
2018-05-04 04:26:25 +00:00
|
|
|
},
|
|
|
|
|
2018-05-04 04:40:32 +00:00
|
|
|
async signMessage(msg, path) {
|
2018-05-04 04:26:25 +00:00
|
|
|
const app = await getEthApp();
|
|
|
|
const msgHex = Buffer.from(msg).toString('hex');
|
|
|
|
const signed = await app.signPersonalMessage(path, msgHex);
|
|
|
|
const combined = addHexPrefix(signed.r + signed.s + signed.v.toString(16));
|
|
|
|
return {
|
|
|
|
signedMessage: combined
|
|
|
|
};
|
2018-05-04 04:40:32 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
async displayAddress(path) {
|
|
|
|
try {
|
|
|
|
const app = await getEthApp();
|
|
|
|
await app.getAddress(path, true, false);
|
|
|
|
return {
|
|
|
|
success: true
|
|
|
|
};
|
|
|
|
} catch (err) {
|
|
|
|
return {
|
|
|
|
success: false
|
|
|
|
};
|
|
|
|
}
|
2018-04-27 20:49:17 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
export default Ledger;
|