mirror of
https://github.com/status-im/MyCrypto.git
synced 2025-01-21 00:19:49 +00:00
3074e50909
* Create popup window for TREZOR pin. * Address PR feedback. Move pin template into its own file loaded via raw-loader. * Fix PIN not unlocking * Dont minify electron main code
122 lines
3.2 KiB
TypeScript
122 lines
3.2 KiB
TypeScript
import BN from 'bn.js';
|
|
import { DeviceList, Session } from 'trezor.js';
|
|
import mapValues from 'lodash/mapValues';
|
|
import { addHexPrefix } from 'ethereumjs-util';
|
|
import EthTx from 'ethereumjs-tx';
|
|
import { WalletLib } from 'shared/enclave/types';
|
|
import { padLeftEven } from 'libs/values';
|
|
import { stripHexPrefixAndLower } from 'libs/formatters';
|
|
import { showPinPrompt } from '../views/pin';
|
|
|
|
const deviceList = new DeviceList({ debug: false });
|
|
|
|
// Keep session in memory so that we're not constantly re-acquiring
|
|
// Null it out if session is grabbed somewhere else first
|
|
let currentSession: Session | null;
|
|
async function getSession() {
|
|
if (currentSession) {
|
|
return currentSession;
|
|
}
|
|
|
|
const { device, session } = await deviceList.acquireFirstDevice(true);
|
|
device.on('disconnect', () => (currentSession = null));
|
|
device.on('changedSessions', (_, isUsedHere) => {
|
|
if (isUsedHere) {
|
|
currentSession = null;
|
|
}
|
|
});
|
|
device.on('pin', (_, cb: (err?: Error, pin?: string) => void) => {
|
|
showPinPrompt()
|
|
.then(pin => {
|
|
cb(undefined, pin);
|
|
})
|
|
.catch(err => {
|
|
console.error('PIN entry failed', err);
|
|
cb(err);
|
|
});
|
|
});
|
|
|
|
currentSession = session;
|
|
return currentSession;
|
|
}
|
|
|
|
const Trezor: WalletLib = {
|
|
async getChainCode(dpath) {
|
|
const session = await getSession();
|
|
const { message } = await session.getPublicKey(parseHDPath(dpath));
|
|
|
|
return {
|
|
chainCode: message.node.chain_code,
|
|
publicKey: message.node.public_key
|
|
};
|
|
},
|
|
|
|
async signTransaction(tx, dpath) {
|
|
const { chainId, ...strTx } = tx;
|
|
const cleanedTx = mapValues(mapValues(strTx, stripHexPrefixAndLower), padLeftEven);
|
|
|
|
const session = await getSession();
|
|
const res = await session.signEthTx(
|
|
parseHDPath(dpath),
|
|
cleanedTx.nonce,
|
|
cleanedTx.gasPrice,
|
|
cleanedTx.gasLimit,
|
|
cleanedTx.to,
|
|
cleanedTx.value,
|
|
cleanedTx.data,
|
|
chainId
|
|
);
|
|
|
|
const signedTx = new EthTx({
|
|
...strTx,
|
|
v: addHexPrefix(new BN(res.v).toString(16)),
|
|
r: addHexPrefix(res.r.toString()),
|
|
s: addHexPrefix(res.s)
|
|
});
|
|
return {
|
|
signedTransaction: signedTx.serialize().toString('hex')
|
|
};
|
|
},
|
|
|
|
async signMessage() {
|
|
throw new Error('Signing is not supported on TREZOR devices');
|
|
},
|
|
|
|
async displayAddress(path) {
|
|
const session = await getSession();
|
|
try {
|
|
await session.ethereumGetAddress(parseHDPath(path), true);
|
|
return { success: true };
|
|
} catch (err) {
|
|
return { success: false };
|
|
}
|
|
}
|
|
};
|
|
|
|
// Lifted from https://github.com/trezor/connect/blob/7919d47ca9d483cf303d77907505ccc7d389c68c/popup/src/utils/path.js#L110
|
|
// tslint:disable no-bitwise
|
|
function parseHDPath(path: string) {
|
|
return path
|
|
.toLowerCase()
|
|
.split('/')
|
|
.filter(p => p !== 'm')
|
|
.map(p => {
|
|
let hardened = false;
|
|
let n = parseInt(p, 10);
|
|
if (p[p.length - 1] === "'") {
|
|
hardened = true;
|
|
p = p.substr(0, p.length - 1);
|
|
}
|
|
if (isNaN(n)) {
|
|
throw new Error('Invalid path specified');
|
|
}
|
|
if (hardened) {
|
|
// hardened index
|
|
n = (n | 0x80000000) >>> 0;
|
|
}
|
|
return n;
|
|
});
|
|
}
|
|
|
|
export default Trezor;
|