eth-dcent-keyring
Advanced tools
Comparing version 0.1.3 to 0.2.0
237
index.js
const { EventEmitter } = require('events') | ||
const { TransactionFactory } = require('@ethereumjs/tx') | ||
const ethUtil = require('ethereumjs-util') | ||
const Transaction = require('ethereumjs-tx') | ||
const DcentConnector = require('dcent-web-connector') | ||
const { getFullPath, getCoinType } = require('./scripts/util') | ||
@@ -10,7 +9,11 @@ const DefaultKeyPathString = `m/44'/60'/0'/0/0` | ||
const DCENT_TIMEOUT = 60000 | ||
const DcentResult = require('./dcent-result') | ||
const DcentResult = { | ||
SUCCESS: 'success', | ||
ERROR: 'error', | ||
} | ||
let LOG | ||
if (process.env.NODE_ENV !== 'production') { | ||
LOG = console.log.bind(console, '[LOG]') | ||
// LOG = console.log.bind(console, '[LOG]') | ||
LOG = () => {} | ||
} else { | ||
@@ -20,2 +23,64 @@ LOG = () => {} | ||
const splitPath = path => { | ||
return path.split('/').filter(item => item.trim() !== '') | ||
} | ||
const hardenedIdx = [1, 2, 3] | ||
const isHardened = idx => { | ||
return hardenedIdx.includes(idx) | ||
} | ||
const getFullPath = (path, idx) => { | ||
const pieces = splitPath(path) | ||
let fullpath = 'm' | ||
for (let i = 1; i < 6; i++) { | ||
if (i < pieces.length) { | ||
fullpath += `/${pieces[i]}` | ||
} else if (i === pieces.length) { | ||
fullpath += `/${idx}` | ||
} else { | ||
fullpath += `/0` | ||
} | ||
if (isHardened(i) && !fullpath.endsWith("'")) { | ||
fullpath += "'" | ||
} | ||
} | ||
return fullpath | ||
} | ||
const coinType = DcentConnector.coinType | ||
const getCoinType = path => { | ||
let type | ||
switch (/m\/44'\/(\d+)'/g.exec(path)[1]) { | ||
case '0': | ||
type = coinType.BITCOIN | ||
break | ||
case '1': | ||
type = coinType.BITCOIN_TESTNET | ||
break | ||
case '60': | ||
type = coinType.ETHEREUM | ||
break | ||
case '137': | ||
type = coinType.RSK | ||
break | ||
case '144': | ||
type = coinType.RIPPLE | ||
break | ||
case '22': | ||
type = coinType.MONACOIN | ||
break | ||
case '8217': | ||
type = coinType.KLAYTN | ||
break | ||
default: | ||
throw new Error('Not Supported path') | ||
} | ||
return type | ||
} | ||
function isOldStyleEthereumjsTx (tx) { | ||
return typeof tx.getChainId === 'function' | ||
} | ||
class DcentKeyring extends EventEmitter { | ||
@@ -168,5 +233,42 @@ constructor (opts = {}) { | ||
signTransaction (address, tx) { | ||
const txObj = this._generateTxObj(tx) | ||
LOG('signTransaction - address', address) | ||
LOG('signTransaction - tx', txObj) | ||
if (isOldStyleEthereumjsTx(tx)) { // old style transaction | ||
tx.v = ethUtil.bufferToHex(tx.getChainId()) | ||
tx.r = '0x00' | ||
tx.s = '0x00' | ||
return this._signTransaction(address, tx.getChainId(), tx) | ||
} | ||
return this._signTransaction( | ||
address, | ||
tx.common.chainIdBN().toNumber(), | ||
tx | ||
) | ||
} | ||
_signTransaction (address, chainId, tx) { | ||
let transaction | ||
if (isOldStyleEthereumjsTx(tx)) { | ||
// legacy transaction from ethereumjs-tx package has no .toJSON() function, | ||
// so we need to convert to hex-strings manually manually | ||
transaction = { | ||
to: this._normalize(tx.to), | ||
value: this._normalize(tx.value), | ||
data: this._normalize(tx.data), | ||
chainId, | ||
nonce: this._normalize(tx.nonce), | ||
gasLimit: this._normalize(tx.gasLimit), | ||
gasPrice: this._normalize(tx.gasPrice), | ||
} | ||
} else { | ||
transaction = { | ||
...tx.toJSON(), | ||
chainId, | ||
to: this._normalize(tx.to), | ||
} | ||
} | ||
transaction.nonce = (transaction.nonce === '0x') ? '0x0' : transaction.nonce | ||
transaction.value = (transaction.value === '0x') ? '0x0' : transaction.value | ||
return new Promise((resolve, reject) => { | ||
@@ -177,22 +279,36 @@ this.unlock() | ||
this.coinType, | ||
txObj.nonce, | ||
txObj.gasPrice, | ||
txObj.gasLimit, | ||
txObj.to, | ||
txObj.value, | ||
txObj.data, | ||
transaction.nonce, | ||
transaction.gasPrice, | ||
transaction.gasLimit, | ||
transaction.to, | ||
transaction.value, | ||
transaction.data, | ||
this.path, // key path | ||
txObj.chainId | ||
transaction.chainId | ||
).then((response) => { | ||
LOG('response - ', response) | ||
if (response.header.status === DcentResult.SUCCESS) { | ||
tx.v = response.body.parameter.sign_v | ||
tx.r = response.body.parameter.sign_r | ||
tx.s = response.body.parameter.sign_s | ||
const signedTx = new Transaction(tx) | ||
const parameter = response.body.parameter | ||
const signedBuffer = Buffer.from(parameter.signed, 'hex') | ||
const tempTx = TransactionFactory.fromSerializedData(signedBuffer) | ||
const addressSignedWith = ethUtil.toChecksumAddress(`0x${signedTx.from.toString('hex')}`) | ||
let signedTx = tx | ||
if (isOldStyleEthereumjsTx(tx)) { | ||
signedTx.v = Buffer.from(ethUtil.stripHexPrefix(parameter.sign_v), 'hex') | ||
signedTx.r = Buffer.from(ethUtil.stripHexPrefix(parameter.sign_r), 'hex') | ||
signedTx.s = Buffer.from(ethUtil.stripHexPrefix(parameter.sign_s), 'hex') | ||
} else { | ||
signedTx = tempTx | ||
} | ||
const addressSignedWith = ethUtil.toChecksumAddress( | ||
ethUtil.addHexPrefix( | ||
tempTx.getSenderAddress().toString('hex'), | ||
), | ||
) | ||
const correctAddress = ethUtil.toChecksumAddress(address) | ||
if (addressSignedWith !== correctAddress) { | ||
reject(new Error('signature doesnt match the right address')) | ||
reject(new Error("signature doesn't match the right address")) | ||
} | ||
LOG('signedTx - ', signedTx) | ||
resolve(signedTx) | ||
@@ -204,4 +320,5 @@ } else if (response.body.error) { | ||
} | ||
}).catch((e) => { | ||
if (e.body.error) { | ||
if (e && e.body && e.body.error) { | ||
reject(new Error(`${e.body.error.code} - ${e.body.error.message}`)) | ||
@@ -222,2 +339,3 @@ } else { | ||
}) | ||
} | ||
@@ -272,3 +390,40 @@ | ||
// Waiting on dcent to enable this | ||
return Promise.reject(new Error('Not supported on this device')) | ||
LOG('signPersonalMessage - withAccount', withAccount) | ||
LOG('signTypedData - typedData', typedData) | ||
return new Promise((resolve, reject) => { | ||
this.unlock() | ||
.then((_) => { | ||
DcentConnector.getEthereumSignedTypedData( | ||
typedData, | ||
this.path | ||
).then((response) => { | ||
if (response.header.status === DcentResult.SUCCESS) { | ||
if (response.body.parameter.address !== ethUtil.toChecksumAddress(withAccount)) { | ||
reject(new Error('signature doesnt match the right address')) | ||
} | ||
resolve(response.body.parameter.sign) | ||
} else if (response.body.error) { | ||
reject(new Error(`${response.body.error.code} - ${response.body.error.message}`)) | ||
} else { | ||
reject(new Error(`Unknown error - ${response}`)) | ||
} | ||
}).catch((e) => { | ||
if (e.body.error) { | ||
reject(new Error(`${e.body.error.code} - ${e.body.error.message}`)) | ||
} else { | ||
reject(new Error(`Unknown error - ${e}`)) | ||
} | ||
}).finally((_) => { | ||
DcentConnector.popupWindowClose() | ||
}) | ||
}).catch((e) => { | ||
if (e && e.body && e.body.error) { | ||
reject(new Error(`${e.body.error.code} - ${e.body.error.message}`)) | ||
} else { | ||
reject(new Error(`Unknown error - ${e}`)) | ||
} | ||
}) | ||
}) | ||
} | ||
@@ -289,15 +444,2 @@ | ||
/* PRIVATE METHODS */ | ||
_generateTxObj (tx) { | ||
const txObj = {} | ||
txObj.nonce = this._normalize(tx.nonce) | ||
txObj.nonce = (txObj.nonce === '0x') ? '0x0' : txObj.nonce | ||
txObj.gasPrice = this._normalize(tx.gasPrice) | ||
txObj.gasLimit = this._normalize(tx.gasLimit) | ||
txObj.to = this._normalize(tx.to) | ||
txObj.value = this._normalize(tx.value) | ||
txObj.value = (txObj.value === '0x') ? '0x0' : txObj.value | ||
txObj.data = this._normalize(tx.data) | ||
txObj.chainId = tx._chainId | ||
return txObj | ||
} | ||
@@ -308,27 +450,2 @@ _normalize (buf) { | ||
// _addressFromIndex (pathBase, i) { | ||
// const dkey = this.hdk.derive(`${pathBase}/${i}`) | ||
// const address = ethUtil | ||
// .publicToAddress(dkey.publicKey, true) | ||
// .toString('hex') | ||
// return ethUtil.toChecksumAddress(address) | ||
// } | ||
// _pathFromAddress (address) { | ||
// const checksummedAddress = ethUtil.toChecksumAddress(address) | ||
// let index = this.paths[checksummedAddress] | ||
// if (typeof index === 'undefined') { | ||
// for (let i = 0; i < MAX_INDEX; i++) { | ||
// if (checksummedAddress === this._addressFromIndex(pathBase, i)) { | ||
// index = i | ||
// break | ||
// } | ||
// } | ||
// } | ||
// if (typeof index === 'undefined') { | ||
// throw new Error('Unknown address') | ||
// } | ||
// return `${this.hdPath}/${index}` | ||
// } | ||
} | ||
@@ -335,0 +452,0 @@ |
{ | ||
"name": "eth-dcent-keyring", | ||
"version": "0.1.3", | ||
"version": "0.2.0", | ||
"description": "A MetaMask compatible keyring, for D'CENT hardware wallets", | ||
"main": "index.js", | ||
"files": [ | ||
"index.js" | ||
], | ||
"scripts": { | ||
"test": "npm run lint && mocha", | ||
"test": "yarn lint && mocha", | ||
"lint": "./node_modules/.bin/eslint . --ext .js", | ||
@@ -32,9 +35,8 @@ "lint:fix": "./node_modules/.bin/eslint --fix . --ext .js" | ||
"dependencies": { | ||
"dcent-web-connector": "^0.8.2", | ||
"eth-sig-util": "^1.4.2", | ||
"ethereumjs-tx": "^1.3.4", | ||
"ethereumjs-util": "^5.1.5", | ||
"events": "^2.0.0" | ||
"@ethereumjs/tx": "3.2.1", | ||
"ethereumjs-util": "7.0.9", | ||
"dcent-web-connector": "^0.10.5" | ||
}, | ||
"devDependencies": { | ||
"@lavamoat/allow-scripts": "^1.0.6", | ||
"assert": "^1.4.1", | ||
@@ -49,4 +51,8 @@ "babel-eslint": "^8.0.0", | ||
"eslint-plugin-mocha": "^5.0.0", | ||
"ethereumjs-tx": "^1.3.4", | ||
"mocha": "^5.0.4" | ||
}, | ||
"engines": { | ||
"node": ">=12.0.0" | ||
} | ||
} |
@@ -7,6 +7,13 @@ # eth-dcent-keyring | ||
- Because the keys are stored in the device, operations that rely on the device will fail if there is no D'CENT device attached, or a different D'CENT device is attached. | ||
- It does not support `signTypedData` or `exportAccount` methods, because D'CENT devices do not support these operations. | ||
- It does not support `exportAccount` methods, because D'CENT devices do not support these operations. | ||
- It works the firmware version 1.3.0+ for D'CENT Biometric device | ||
- It returns only one account. | ||
## Install | ||
Run the following command: | ||
``` | ||
yarn add eth-dcent-keyring | ||
``` | ||
## Testing | ||
@@ -16,3 +23,5 @@ Run the following command: | ||
``` | ||
npm run test | ||
yarn | ||
yarn test | ||
``` | ||
@@ -19,0 +28,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
3
28
16460
12
4
402
1
+ Added@ethereumjs/tx@3.2.1
+ Added@ethereumjs/common@2.6.5(transitive)
+ Added@ethereumjs/tx@3.2.1(transitive)
+ Added@types/bn.js@5.1.6(transitive)
+ Addedcrc-32@1.2.2(transitive)
+ Addeddcent-web-connector@0.10.5(transitive)
+ Addedethereumjs-util@7.0.97.1.5(transitive)
- Removedeth-sig-util@^1.4.2
- Removedethereumjs-tx@^1.3.4
- Removedevents@^2.0.0
- Removeddcent-web-connector@0.8.2(transitive)
- Removedeth-sig-util@1.4.2(transitive)
- Removedethereum-common@0.0.18(transitive)
- Removedethereumjs-tx@1.3.7(transitive)
- Removedethereumjs-util@5.2.1(transitive)
- Removedevents@2.1.0(transitive)
Updateddcent-web-connector@^0.10.5
Updatedethereumjs-util@7.0.9