diff options
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | src/0x.js.ts | 64 | ||||
-rw-r--r-- | src/globals.d.ts | 3 | ||||
-rw-r--r-- | src/types.ts | 1 | ||||
-rw-r--r-- | src/utils/utils.ts | 4 |
5 files changed, 73 insertions, 1 deletions
diff --git a/package.json b/package.json index 5c352bc88..a191d4b73 100644 --- a/package.json +++ b/package.json @@ -68,9 +68,11 @@ }, "dependencies": { "bignumber.js": "^4.0.2", + "compare-versions": "^3.0.1", "es6-promisify": "^5.0.0", "ethereumjs-abi": "^0.6.4", "ethereumjs-util": "^5.1.1", + "find-versions": "^2.0.0", "jsonschema": "^1.1.1", "lodash": "^4.17.4", "truffle-contract": "^2.0.0", diff --git a/src/0x.js.ts b/src/0x.js.ts index de082146f..eff371c3b 100644 --- a/src/0x.js.ts +++ b/src/0x.js.ts @@ -8,9 +8,11 @@ import {Web3Wrapper} from './web3_wrapper'; import {constants} from './utils/constants'; import {utils} from './utils/utils'; import {assert} from './utils/assert'; +import findVersions = require('find-versions'); +import compareVersions = require('compare-versions'); import {ExchangeWrapper} from './contract_wrappers/exchange_wrapper'; import {ECSignatureSchema} from './schemas/ec_signature_schema'; -import {SolidityTypes, ECSignature} from './types'; +import {SolidityTypes, ECSignature, ZeroExError} from './types'; const MAX_DIGITS_IN_UNSIGNED_256_INT = 78; @@ -131,4 +133,64 @@ export class ZeroEx { this.web3Wrapper = new Web3Wrapper(web3); this.exchange = new ExchangeWrapper(this.web3Wrapper); } + /** + * Signs an orderHash and returns it's ECSignature + * This method currently supports TestRPC, Geth and Parity above and below V1.6.6 + */ + public async signOrderHashAsync(orderHashHex: string): Promise<ECSignature> { + assert.isHexString('orderHashHex', orderHashHex); + + let msgHashHex; + const nodeVersion = await this.web3Wrapper.getNodeVersionAsync(); + const isParityNode = utils.isParityNode(nodeVersion); + if (isParityNode) { + // Parity node adds the personalMessage prefix itself + msgHashHex = orderHashHex; + } else { + const orderHashBuff = ethUtil.toBuffer(orderHashHex); + const msgHashBuff = ethUtil.hashPersonalMessage(orderHashBuff); + msgHashHex = ethUtil.bufferToHex(msgHashBuff); + } + + const makerAddressIfExists = await this.web3Wrapper.getSenderAddressIfExistsAsync(); + if (_.isUndefined(makerAddressIfExists)) { + throw new Error(ZeroExError.USER_HAS_NO_ASSOCIATED_ADDRESSES); + } + + const signature = await this.web3Wrapper.signTransactionAsync(makerAddressIfExists, msgHashHex); + + let signatureData; + const [nodeVersionNumber] = findVersions(nodeVersion); + // Parity v1.6.6 and earlier returns the signatureData as vrs instead of rsv as Geth does + // Later versions return rsv but for the time being we still want to support version < 1.6.6 + // Date: May 23rd 2017 + const latestParityVersionWithVRS = '1.6.6'; + const isVersionBeforeParityFix = compareVersions(nodeVersionNumber, latestParityVersionWithVRS) <= 0; + if (isParityNode && isVersionBeforeParityFix) { + const signatureBuffer = ethUtil.toBuffer(signature); + let v = signatureBuffer[0]; + if (v < 27) { + v += 27; + } + signatureData = { + v, + r: signatureBuffer.slice(1, 33), + s: signatureBuffer.slice(33, 65), + }; + } else { + signatureData = ethUtil.fromRpcSig(signature); + } + + const {v, r, s} = signatureData; + const ecSignature: ECSignature = { + v, + r: ethUtil.bufferToHex(r), + s: ethUtil.bufferToHex(s), + }; + const isValidSignature = ZeroEx.isValidSignature(orderHashHex, ecSignature, makerAddressIfExists); + if (!isValidSignature) { + throw new Error(ZeroExError.INVALID_SIGNATURE); + } + return ecSignature; + } } diff --git a/src/globals.d.ts b/src/globals.d.ts index dee957f2f..78b768dd1 100644 --- a/src/globals.d.ts +++ b/src/globals.d.ts @@ -3,6 +3,8 @@ declare module 'bn.js'; declare module 'request-promise-native'; declare module 'web3-provider-engine'; declare module 'web3-provider-engine/subproviders/rpc'; +declare module 'find-versions'; +declare module 'compare-versions'; declare interface Schema { id: string; @@ -35,6 +37,7 @@ declare module 'ethereumjs-util' { const pubToAddress: (pubKey: string) => Buffer; const isValidAddress: (address: string) => boolean; const bufferToInt: (buffer: Buffer) => number; + const fromRpcSig: (signature: string) => {v: number, r: Buffer, s: Buffer}; } // truffle-contract declarations diff --git a/src/types.ts b/src/types.ts index 4da03a4d3..3bed01547 100644 --- a/src/types.ts +++ b/src/types.ts @@ -13,6 +13,7 @@ export const ZeroExError = strEnum([ 'CONTRACT_DOES_NOT_EXIST', 'UNHANDLED_ERROR', 'USER_HAS_NO_ASSOCIATED_ADDRESSES', + 'INVALID_SIGNATURE', ]); export type ZeroExError = keyof typeof ZeroExError; diff --git a/src/utils/utils.ts b/src/utils/utils.ts index b514b702d..2098a67b3 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -1,3 +1,4 @@ +import * as _ from 'lodash'; import * as BN from 'bn.js'; export const utils = { @@ -15,4 +16,7 @@ export const utils = { console.log(message); /* tslint:enable */ }, + isParityNode(nodeVersion: string): boolean { + return _.includes(nodeVersion, 'Parity'); + }, }; |