aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJacob Evans <dekz@dekz.net>2018-08-14 07:36:11 +0800
committerGitHub <noreply@github.com>2018-08-14 07:36:11 +0800
commite51deb93eef2e59a3577195becfb6d602df3892a (patch)
treea74ca989588877d3552f28144d40b015f1f5b67e
parent0025c6640b3a5840bacfb4ab5f6e937fc9af8eff (diff)
parenta3517574936aa6a4911003dbff06302926b04cb4 (diff)
downloaddexon-sol-tools-e51deb93eef2e59a3577195becfb6d602df3892a.tar.gz
dexon-sol-tools-e51deb93eef2e59a3577195becfb6d602df3892a.tar.zst
dexon-sol-tools-e51deb93eef2e59a3577195becfb6d602df3892a.zip
Merge pull request #914 from 0xProject/feature/0x.js/ec-signature-string
0x.js Return signature as a hex encoded string
-rw-r--r--packages/0x.js/CHANGELOG.json9
-rw-r--r--packages/0x.js/src/0x.ts15
-rw-r--r--packages/0x.js/src/index.ts2
-rw-r--r--packages/contracts/test/exchange/signature_validator.ts12
-rw-r--r--packages/order-utils/CHANGELOG.json10
-rw-r--r--packages/order-utils/src/index.ts10
-rw-r--r--packages/order-utils/src/order_factory.ts25
-rw-r--r--packages/order-utils/src/signature_utils.ts109
-rw-r--r--packages/order-utils/src/types.ts22
-rw-r--r--packages/order-utils/test/signature_utils_test.ts129
-rw-r--r--packages/subproviders/test/utils/fixture_data.ts2
-rw-r--r--packages/types/CHANGELOG.json9
-rw-r--r--packages/types/src/index.ts11
13 files changed, 225 insertions, 140 deletions
diff --git a/packages/0x.js/CHANGELOG.json b/packages/0x.js/CHANGELOG.json
index 913b7a76e..a1bdcc506 100644
--- a/packages/0x.js/CHANGELOG.json
+++ b/packages/0x.js/CHANGELOG.json
@@ -1,5 +1,14 @@
[
{
+ "version": "1.0.1-rc.4",
+ "changes": [
+ {
+ "pr": 914,
+ "note": "Update ecSignOrderHashAsync to return the signature as a string for immediate use in contracts"
+ }
+ ]
+ },
+ {
"version": "1.0.1-rc.3",
"changes": [
{
diff --git a/packages/0x.js/src/0x.ts b/packages/0x.js/src/0x.ts
index 2a2b82f63..48d00c1ac 100644
--- a/packages/0x.js/src/0x.ts
+++ b/packages/0x.js/src/0x.ts
@@ -14,13 +14,12 @@ import {
ecSignOrderHashAsync,
generatePseudoRandomSalt,
isValidSignatureAsync,
- MessagePrefixOpts,
orderHashUtils,
} from '@0xproject/order-utils';
// HACK: Since we export assetDataUtils from ZeroEx and it has AssetProxyId, ERC20AssetData and ERC721AssetData
// in it's public interface, we need to import these types here.
// tslint:disable-next-line:no-unused-variable
-import { AssetProxyId, ECSignature, ERC20AssetData, ERC721AssetData, Order, SignedOrder } from '@0xproject/types';
+import { AssetProxyId, ERC20AssetData, ERC721AssetData, Order, SignedOrder, SignerType } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
@@ -238,19 +237,21 @@ export class ZeroEx {
* @param orderHash Hex encoded orderHash to sign.
* @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address
* must be available via the Provider supplied to 0x.js.
- * @param MessagePrefixOpts Options regarding the desired prefix and whether to add it before calling `eth_sign`
- * @return An object containing the Elliptic curve signature parameters generated by signing the orderHash.
+ * @param signerType the signer type that will perform the `eth_sign` operation. E.g Default, Metamask, Ledger or Trezor.
+ * Some implementations exhibit different behaviour. Default will assume a spec compliant eth_sign implementation.
+ * This parameter is defaulted to `SignerType.Default`.
+ * @return A hex encoded string of the Elliptic curve signature parameters generated by signing the orderHash and signature type.
*/
public async ecSignOrderHashAsync(
orderHash: string,
signerAddress: string,
- messagePrefixOpts: MessagePrefixOpts,
- ): Promise<ECSignature> {
+ signerType: SignerType = SignerType.Default,
+ ): Promise<string> {
const signature = await ecSignOrderHashAsync(
this._contractWrappers.getProvider(),
orderHash,
signerAddress,
- messagePrefixOpts,
+ signerType,
);
return signature;
}
diff --git a/packages/0x.js/src/index.ts b/packages/0x.js/src/index.ts
index 95ca07eea..2ba60e730 100644
--- a/packages/0x.js/src/index.ts
+++ b/packages/0x.js/src/index.ts
@@ -1,12 +1,12 @@
export { ZeroEx } from './0x';
-export { MessagePrefixType, MessagePrefixOpts } from '@0xproject/order-utils';
export { Web3ProviderEngine, RPCSubprovider } from '@0xproject/subproviders';
export {
ExchangeContractErrs,
Order,
SignedOrder,
+ SignerType,
ECSignature,
OrderStateValid,
OrderStateInvalid,
diff --git a/packages/contracts/test/exchange/signature_validator.ts b/packages/contracts/test/exchange/signature_validator.ts
index bef0547bd..56a06c247 100644
--- a/packages/contracts/test/exchange/signature_validator.ts
+++ b/packages/contracts/test/exchange/signature_validator.ts
@@ -1,6 +1,6 @@
import { BlockchainLifecycle } from '@0xproject/dev-utils';
-import { addSignedMessagePrefix, assetDataUtils, MessagePrefixType, orderHashUtils } from '@0xproject/order-utils';
-import { RevertReason, SignatureType, SignedOrder } from '@0xproject/types';
+import { addSignedMessagePrefix, assetDataUtils, orderHashUtils } from '@0xproject/order-utils';
+import { RevertReason, SignatureType, SignedOrder, SignerType } from '@0xproject/types';
import * as chai from 'chai';
import { LogWithDecodedArgs } from 'ethereum-types';
import ethUtil = require('ethereumjs-util');
@@ -213,7 +213,7 @@ describe('MixinSignatureValidator', () => {
it('should return true when SignatureType=EthSign and signature is valid', async () => {
// Create EthSign signature
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
- const orderHashWithEthSignPrefixHex = addSignedMessagePrefix(orderHashHex, MessagePrefixType.EthSign);
+ const orderHashWithEthSignPrefixHex = addSignedMessagePrefix(orderHashHex, SignerType.Default);
const orderHashWithEthSignPrefixBuffer = ethUtil.toBuffer(orderHashWithEthSignPrefixHex);
const ecSignature = ethUtil.ecsign(orderHashWithEthSignPrefixBuffer, signerPrivateKey);
// Create 0x signature from EthSign signature
@@ -236,7 +236,7 @@ describe('MixinSignatureValidator', () => {
it('should return false when SignatureType=EthSign and signature is invalid', async () => {
// Create EthSign signature
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
- const orderHashWithEthSignPrefixHex = addSignedMessagePrefix(orderHashHex, MessagePrefixType.EthSign);
+ const orderHashWithEthSignPrefixHex = addSignedMessagePrefix(orderHashHex, SignerType.Default);
const orderHashWithEthSignPrefixBuffer = ethUtil.toBuffer(orderHashWithEthSignPrefixHex);
const ecSignature = ethUtil.ecsign(orderHashWithEthSignPrefixBuffer, signerPrivateKey);
// Create 0x signature from EthSign signature
@@ -385,7 +385,7 @@ describe('MixinSignatureValidator', () => {
it('should return true when SignatureType=Trezor and signature is valid', async () => {
// Create Trezor signature
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
- const orderHashWithTrezorPrefixHex = addSignedMessagePrefix(orderHashHex, MessagePrefixType.Trezor);
+ const orderHashWithTrezorPrefixHex = addSignedMessagePrefix(orderHashHex, SignerType.Trezor);
const orderHashWithTrezorPrefixBuffer = ethUtil.toBuffer(orderHashWithTrezorPrefixHex);
const ecSignature = ethUtil.ecsign(orderHashWithTrezorPrefixBuffer, signerPrivateKey);
// Create 0x signature from Trezor signature
@@ -408,7 +408,7 @@ describe('MixinSignatureValidator', () => {
it('should return false when SignatureType=Trezor and signature is invalid', async () => {
// Create Trezor signature
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
- const orderHashWithTrezorPrefixHex = addSignedMessagePrefix(orderHashHex, MessagePrefixType.Trezor);
+ const orderHashWithTrezorPrefixHex = addSignedMessagePrefix(orderHashHex, SignerType.Trezor);
const orderHashWithTrezorPrefixBuffer = ethUtil.toBuffer(orderHashWithTrezorPrefixHex);
const ecSignature = ethUtil.ecsign(orderHashWithTrezorPrefixBuffer, signerPrivateKey);
// Create 0x signature from Trezor signature
diff --git a/packages/order-utils/CHANGELOG.json b/packages/order-utils/CHANGELOG.json
index fa82976ad..6f59580b3 100644
--- a/packages/order-utils/CHANGELOG.json
+++ b/packages/order-utils/CHANGELOG.json
@@ -1,5 +1,15 @@
[
{
+ "version": "1.0.1-rc.4",
+ "changes": [
+ {
+ "pr": 914,
+ "note":
+ "Update ecSignOrderHashAsync to return signature string with signature type byte. Removes messagePrefixOpts."
+ }
+ ]
+ },
+ {
"version": "1.0.1-rc.3",
"changes": [
{
diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts
index 858f500c6..681fbc904 100644
--- a/packages/order-utils/src/index.ts
+++ b/packages/order-utils/src/index.ts
@@ -13,15 +13,7 @@ export { orderFactory } from './order_factory';
export { constants } from './constants';
export { crypto } from './crypto';
export { generatePseudoRandomSalt } from './salt';
-export {
- CreateOrderOpts,
- OrderError,
- MessagePrefixType,
- MessagePrefixOpts,
- EIP712Parameter,
- EIP712Schema,
- EIP712Types,
-} from './types';
+export { CreateOrderOpts, OrderError, EIP712Parameter, EIP712Schema, EIP712Types } from './types';
export { AbstractBalanceAndProxyAllowanceFetcher } from './abstract/abstract_balance_and_proxy_allowance_fetcher';
export { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_filled_cancelled_fetcher';
export { BalanceAndProxyAllowanceLazyStore } from './store/balance_and_proxy_allowance_lazy_store';
diff --git a/packages/order-utils/src/order_factory.ts b/packages/order-utils/src/order_factory.ts
index 14727fd97..4a6f3924b 100644
--- a/packages/order-utils/src/order_factory.ts
+++ b/packages/order-utils/src/order_factory.ts
@@ -1,14 +1,13 @@
-import { ECSignature, Order, SignedOrder } from '@0xproject/types';
+import { Order, SignedOrder, SignerType } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import { Provider } from 'ethereum-types';
-import * as ethUtil from 'ethereumjs-util';
import * as _ from 'lodash';
import { constants } from './constants';
import { orderHashUtils } from './order_hash';
import { generatePseudoRandomSalt } from './salt';
import { ecSignOrderHashAsync } from './signature_utils';
-import { CreateOrderOpts, MessagePrefixType } from './types';
+import { CreateOrderOpts } from './types';
export const orderFactory = {
createOrder(
@@ -59,12 +58,7 @@ export const orderFactory = {
createOrderOpts,
);
const orderHash = orderHashUtils.getOrderHashHex(order);
- const messagePrefixOpts = {
- prefixType: MessagePrefixType.EthSign,
- shouldAddPrefixBeforeCallingEthSign: false,
- };
- const ecSignature = await ecSignOrderHashAsync(provider, orderHash, makerAddress, messagePrefixOpts);
- const signature = getVRSHexString(ecSignature);
+ const signature = await ecSignOrderHashAsync(provider, orderHash, makerAddress, SignerType.Default);
const signedOrder: SignedOrder = _.assign(order, { signature });
return signedOrder;
},
@@ -89,16 +83,3 @@ function generateDefaultCreateOrderOpts(): {
expirationTimeSeconds: constants.INFINITE_TIMESTAMP_SEC,
};
}
-
-function getVRSHexString(ecSignature: ECSignature): string {
- const ETH_SIGN_SIGNATURE_TYPE = '03';
- const vrs = `${intToHex(ecSignature.v)}${ethUtil.stripHexPrefix(ecSignature.r)}${ethUtil.stripHexPrefix(
- ecSignature.s,
- )}${ETH_SIGN_SIGNATURE_TYPE}`;
- return vrs;
-}
-
-function intToHex(i: number): string {
- const hex = ethUtil.bufferToHex(ethUtil.toBuffer(i));
- return hex;
-}
diff --git a/packages/order-utils/src/signature_utils.ts b/packages/order-utils/src/signature_utils.ts
index 26fb24705..870aef2ed 100644
--- a/packages/order-utils/src/signature_utils.ts
+++ b/packages/order-utils/src/signature_utils.ts
@@ -1,5 +1,5 @@
import { schemas } from '@0xproject/json-schemas';
-import { ECSignature, SignatureType, ValidatorSignature } from '@0xproject/types';
+import { ECSignature, SignatureType, SignerType, ValidatorSignature } from '@0xproject/types';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { Provider } from 'ethereum-types';
import * as ethUtil from 'ethereumjs-util';
@@ -10,7 +10,7 @@ import { assert } from './assert';
import { ExchangeContract } from './generated_contract_wrappers/exchange';
import { IValidatorContract } from './generated_contract_wrappers/i_validator';
import { IWalletContract } from './generated_contract_wrappers/i_wallet';
-import { MessagePrefixOpts, MessagePrefixType, OrderError } from './types';
+import { OrderError } from './types';
import { utils } from './utils';
/**
@@ -48,7 +48,7 @@ export async function isValidSignatureAsync(
case SignatureType.EthSign: {
const ecSignature = parseECSignature(signature);
- const prefixedMessageHex = addSignedMessagePrefix(data, MessagePrefixType.EthSign);
+ const prefixedMessageHex = addSignedMessagePrefix(data, SignerType.Default);
return isValidECSignature(prefixedMessageHex, ecSignature, signerAddress);
}
@@ -72,7 +72,7 @@ export async function isValidSignatureAsync(
}
case SignatureType.Trezor: {
- const prefixedMessageHex = addSignedMessagePrefix(data, MessagePrefixType.Trezor);
+ const prefixedMessageHex = addSignedMessagePrefix(data, SignerType.Trezor);
const ecSignature = parseECSignature(signature);
return isValidECSignature(prefixedMessageHex, ecSignature, signerAddress);
}
@@ -191,22 +191,21 @@ export function isValidECSignature(data: string, signature: ECSignature, signerA
}
/**
- * Signs an orderHash and returns it's elliptic curve signature.
+ * Signs an orderHash and returns it's elliptic curve signature and signature type.
* This method currently supports TestRPC, Geth and Parity above and below V1.6.6
* @param orderHash Hex encoded orderHash to sign.
* @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address
* must be available via the Provider supplied to 0x.js.
- * @param hashPrefixOpts Different signers add/require different prefixes be appended to the message being signed.
- * Since we cannot know ahead of time which signer you are using, you must supply both a prefixType and
- * whether it must be added before calling `eth_sign` (some signers add it themselves)
- * @return An object containing the Elliptic curve signature parameters generated by signing the orderHash.
+ * @param signerType Different signers add/require different prefixes to be prepended to the message being signed.
+ * Since we cannot know ahead of time which signer you are using, you must supply a SignerType.
+ * @return A hex encoded string containing the Elliptic curve signature generated by signing the orderHash and the Signature Type.
*/
export async function ecSignOrderHashAsync(
provider: Provider,
orderHash: string,
signerAddress: string,
- messagePrefixOpts: MessagePrefixOpts,
-): Promise<ECSignature> {
+ signerType: SignerType,
+): Promise<string> {
assert.isWeb3Provider('provider', provider);
assert.isHexString('orderHash', orderHash);
assert.isETHAddressHex('signerAddress', signerAddress);
@@ -215,8 +214,10 @@ export async function ecSignOrderHashAsync(
const normalizedSignerAddress = signerAddress.toLowerCase();
let msgHashHex = orderHash;
- const prefixedMsgHashHex = addSignedMessagePrefix(orderHash, messagePrefixOpts.prefixType);
- if (messagePrefixOpts.shouldAddPrefixBeforeCallingEthSign) {
+ const prefixedMsgHashHex = addSignedMessagePrefix(orderHash, signerType);
+ // Metamask incorrectly implements eth_sign and does not prefix the message as per the spec
+ // Source: https://github.com/MetaMask/metamask-extension/commit/a9d36860bec424dcee8db043d3e7da6a5ff5672e
+ if (signerType === SignerType.Metamask) {
msgHashHex = prefixedMsgHashHex;
}
const signature = await web3Wrapper.signMessageAsync(normalizedSignerAddress, msgHashHex);
@@ -225,57 +226,97 @@ export async function ecSignOrderHashAsync(
// v + r + s OR r + s + v, and different clients (even different versions of the same client)
// return the signature params in different orders. In order to support all client implementations,
// we parse the signature in both ways, and evaluate if either one is a valid signature.
+ // r + s + v is the most prevalent format from eth_sign, so we attempt this first.
// tslint:disable-next-line:custom-no-magic-numbers
const validVParamValues = [27, 28];
+ const ecSignatureRSV = parseSignatureHexAsRSV(signature);
+ if (_.includes(validVParamValues, ecSignatureRSV.v)) {
+ const isValidRSVSignature = isValidECSignature(prefixedMsgHashHex, ecSignatureRSV, normalizedSignerAddress);
+ if (isValidRSVSignature) {
+ const convertedSignatureHex = convertECSignatureToSignatureHex(ecSignatureRSV, signerType);
+ return convertedSignatureHex;
+ }
+ }
const ecSignatureVRS = parseSignatureHexAsVRS(signature);
if (_.includes(validVParamValues, ecSignatureVRS.v)) {
const isValidVRSSignature = isValidECSignature(prefixedMsgHashHex, ecSignatureVRS, normalizedSignerAddress);
if (isValidVRSSignature) {
- return ecSignatureVRS;
+ const convertedSignatureHex = convertECSignatureToSignatureHex(ecSignatureVRS, signerType);
+ return convertedSignatureHex;
}
}
- const ecSignatureRSV = parseSignatureHexAsRSV(signature);
- if (_.includes(validVParamValues, ecSignatureRSV.v)) {
- const isValidRSVSignature = isValidECSignature(prefixedMsgHashHex, ecSignatureRSV, normalizedSignerAddress);
- if (isValidRSVSignature) {
- return ecSignatureRSV;
+ throw new Error(OrderError.InvalidSignature);
+}
+/**
+ * Combines ECSignature with V,R,S and the relevant signature type for use in 0x protocol
+ * @param ecSignature The ECSignature of the signed data
+ * @param signerType The SignerType of the signed data
+ * @return Hex encoded string of signature (v,r,s) with Signature Type
+ */
+export function convertECSignatureToSignatureHex(ecSignature: ECSignature, signerType: SignerType): string {
+ const signatureBuffer = Buffer.concat([
+ ethUtil.toBuffer(ecSignature.v),
+ ethUtil.toBuffer(ecSignature.r),
+ ethUtil.toBuffer(ecSignature.s),
+ ]);
+ const signatureHex = `0x${signatureBuffer.toString('hex')}`;
+ let signatureType;
+ switch (signerType) {
+ case SignerType.Metamask:
+ case SignerType.Ledger:
+ case SignerType.Default: {
+ signatureType = SignatureType.EthSign;
+ break;
+ }
+ case SignerType.Trezor: {
+ signatureType = SignatureType.Trezor;
+ break;
}
+ default:
+ throw new Error(`Unrecognized SignerType: ${signerType}`);
}
-
- throw new Error(OrderError.InvalidSignature);
+ const signatureWithType = convertToSignatureWithType(signatureHex, signatureType);
+ return signatureWithType;
+}
+/**
+ * Combines the signature proof and the Signature Type.
+ * @param signature The hex encoded signature proof
+ * @param signatureType The signature type, i.e EthSign, Trezor, Wallet etc.
+ * @return Hex encoded string of signature proof with Signature Type
+ */
+export function convertToSignatureWithType(signature: string, signatureType: SignatureType): string {
+ const signatureBuffer = Buffer.concat([ethUtil.toBuffer(signature), ethUtil.toBuffer(signatureType)]);
+ const signatureHex = `0x${signatureBuffer.toString('hex')}`;
+ return signatureHex;
}
-
/**
* Adds the relevant prefix to the message being signed.
* @param message Message to sign
- * @param messagePrefixType The type of message prefix to add. Different signers expect
+ * @param signerType The type of message prefix to add for a given SignerType. Different signers expect
* specific message prefixes.
* @return Prefixed message
*/
-export function addSignedMessagePrefix(message: string, messagePrefixType: MessagePrefixType): string {
+export function addSignedMessagePrefix(message: string, signerType: SignerType = SignerType.Default): string {
assert.isString('message', message);
- assert.doesBelongToStringEnum('messagePrefixType', messagePrefixType, MessagePrefixType);
- switch (messagePrefixType) {
- case MessagePrefixType.None:
- return message;
-
- case MessagePrefixType.EthSign: {
+ assert.doesBelongToStringEnum('signerType', signerType, SignerType);
+ switch (signerType) {
+ case SignerType.Metamask:
+ case SignerType.Ledger:
+ case SignerType.Default: {
const msgBuff = ethUtil.toBuffer(message);
const prefixedMsgBuff = ethUtil.hashPersonalMessage(msgBuff);
const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff);
return prefixedMsgHex;
}
-
- case MessagePrefixType.Trezor: {
+ case SignerType.Trezor: {
const msgBuff = ethUtil.toBuffer(message);
const prefixedMsgBuff = hashTrezorPersonalMessage(msgBuff);
const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff);
return prefixedMsgHex;
}
-
default:
- throw new Error(`Unrecognized MessagePrefixType: ${messagePrefixType}`);
+ throw new Error(`Unrecognized SignerType: ${signerType}`);
}
}
diff --git a/packages/order-utils/src/types.ts b/packages/order-utils/src/types.ts
index f44e94349..1fbd8cf7b 100644
--- a/packages/order-utils/src/types.ts
+++ b/packages/order-utils/src/types.ts
@@ -4,28 +4,6 @@ export enum OrderError {
InvalidSignature = 'INVALID_SIGNATURE',
}
-/**
- * The requisite message prefix (is any) to add to an `eth_sign` request.
- */
-export enum MessagePrefixType {
- None = 'NONE',
- EthSign = 'ETH_SIGN',
- Trezor = 'TREZOR',
-}
-
-/**
- * Options related to message prefixing of messages sent to `eth_sign`
- * Some signers prepend a message prefix (e.g Parity Signer, Ledger, TestRPC), while
- * others require it already be prepended (e.g Metamask). In addition, different signers
- * expect slightly different prefixes (See: https://github.com/ethereum/go-ethereum/issues/14794).
- * Depending on the signer that will receive your signing request, you must specify the
- * desired prefix and whether it should be added before making the `eth_sign` request.
- */
-export interface MessagePrefixOpts {
- prefixType: MessagePrefixType;
- shouldAddPrefixBeforeCallingEthSign: boolean;
-}
-
export enum TradeSide {
Maker = 'maker',
Taker = 'taker',
diff --git a/packages/order-utils/test/signature_utils_test.ts b/packages/order-utils/test/signature_utils_test.ts
index baae2b414..a25d2afd6 100644
--- a/packages/order-utils/test/signature_utils_test.ts
+++ b/packages/order-utils/test/signature_utils_test.ts
@@ -1,3 +1,4 @@
+import { SignerType } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import * as chai from 'chai';
import { JSONRPCErrorCallback, JSONRPCRequestPayload } from 'ethereum-types';
@@ -5,8 +6,8 @@ import * as _ from 'lodash';
import 'mocha';
import * as Sinon from 'sinon';
-import { ecSignOrderHashAsync, generatePseudoRandomSalt, MessagePrefixType } from '../src';
-import { isValidECSignature, isValidSignatureAsync } from '../src/signature_utils';
+import { ecSignOrderHashAsync, generatePseudoRandomSalt } from '../src';
+import { convertECSignatureToSignatureHex, isValidECSignature, isValidSignatureAsync } from '../src/signature_utils';
import { chaiSetup } from './utils/chai_setup';
import { provider, web3Wrapper } from './utils/web3_wrapper';
@@ -119,32 +120,23 @@ describe('Signature utils', () => {
_.each(stubs, s => s.restore());
stubs = [];
});
- it('Should return the correct ECSignature', async () => {
+ it('Should return the correct Signature', async () => {
const orderHash = '0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0';
- const expectedECSignature = {
- v: 27,
- r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
- s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
- };
- const messagePrefixOpts = {
- prefixType: MessagePrefixType.EthSign,
- shouldAddPrefixBeforeCallingEthSign: false,
- };
- const ecSignature = await ecSignOrderHashAsync(provider, orderHash, makerAddress, messagePrefixOpts);
- expect(ecSignature).to.deep.equal(expectedECSignature);
+ const expectedSignature =
+ '0x1b61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc3340349190569279751135161d22529dc25add4f6069af05be04cacbda2ace225403';
+ const ecSignature = await ecSignOrderHashAsync(provider, orderHash, makerAddress, SignerType.Default);
+ expect(ecSignature).to.equal(expectedSignature);
});
- it('should return the correct ECSignature for signatureHex concatenated as R + S + V', async () => {
+ it('should return the correct Signature for signatureHex concatenated as R + S + V', async () => {
const orderHash = '0x34decbedc118904df65f379a175bb39ca18209d6ce41d5ed549d54e6e0a95004';
- const expectedECSignature = {
- v: 27,
- r: '0x117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d87287113',
- s: '0x7feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b',
- };
+ const expectedSignature =
+ '0x1b117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d872871137feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b03';
const fakeProvider = {
async sendAsync(payload: JSONRPCRequestPayload, callback: JSONRPCErrorCallback): Promise<void> {
if (payload.method === 'eth_sign') {
const [address, message] = payload.params;
+ expect(message).to.equal(orderHash);
const signature = await web3Wrapper.signMessageAsync(address, message);
// tslint:disable-next-line:custom-no-magic-numbers
const rsvHex = `0x${signature.substr(130)}${signature.substr(2, 128)}`;
@@ -158,21 +150,13 @@ describe('Signature utils', () => {
}
},
};
-
- const messagePrefixOpts = {
- prefixType: MessagePrefixType.EthSign,
- shouldAddPrefixBeforeCallingEthSign: false,
- };
- const ecSignature = await ecSignOrderHashAsync(fakeProvider, orderHash, makerAddress, messagePrefixOpts);
- expect(ecSignature).to.deep.equal(expectedECSignature);
+ const ecSignature = await ecSignOrderHashAsync(fakeProvider, orderHash, makerAddress, SignerType.Default);
+ expect(ecSignature).to.equal(expectedSignature);
});
- it('should return the correct ECSignature for signatureHex concatenated as V + R + S', async () => {
+ it('should return the correct Signature for signatureHex concatenated as V + R + S', async () => {
const orderHash = '0x34decbedc118904df65f379a175bb39ca18209d6ce41d5ed549d54e6e0a95004';
- const expectedECSignature = {
- v: 27,
- r: '0x117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d87287113',
- s: '0x7feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b',
- };
+ const expectedSignature =
+ '0x1b117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d872871137feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b03';
const fakeProvider = {
async sendAsync(payload: JSONRPCRequestPayload, callback: JSONRPCErrorCallback): Promise<void> {
if (payload.method === 'eth_sign') {
@@ -189,12 +173,81 @@ describe('Signature utils', () => {
},
};
- const messagePrefixOpts = {
- prefixType: MessagePrefixType.EthSign,
- shouldAddPrefixBeforeCallingEthSign: false,
+ const ecSignature = await ecSignOrderHashAsync(fakeProvider, orderHash, makerAddress, SignerType.Default);
+ expect(ecSignature).to.equal(expectedSignature);
+ });
+ // Note this is due to a bug in Metamask where it does not prefix before signing, this is a known issue and is to be fixed in the future
+ // Source: https://github.com/MetaMask/metamask-extension/commit/a9d36860bec424dcee8db043d3e7da6a5ff5672e
+ it('should receive a payload modified with a prefix when Metamask is SignerType', async () => {
+ const orderHash = '0x34decbedc118904df65f379a175bb39ca18209d6ce41d5ed549d54e6e0a95004';
+ const orderHashPrefixed = '0xae70f31d26096291aa681b26cb7574563956221d0b4213631e1ef9df675d4cba';
+ const expectedSignature =
+ '0x1b117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d872871137feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b03';
+ // Generated from a MM eth_sign request from 0x5409ed021d9299bf6814279a6a1411a7e866a631 signing 0xae70f31d26096291aa681b26cb7574563956221d0b4213631e1ef9df675d4cba
+ const metamaskSignature =
+ '0x117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d872871137feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b1b';
+ const fakeProvider = {
+ async sendAsync(payload: JSONRPCRequestPayload, callback: JSONRPCErrorCallback): Promise<void> {
+ if (payload.method === 'eth_sign') {
+ const [, message] = payload.params;
+ expect(message).to.equal(orderHashPrefixed);
+ callback(null, {
+ id: 42,
+ jsonrpc: '2.0',
+ result: metamaskSignature,
+ });
+ } else {
+ callback(null, { id: 42, jsonrpc: '2.0', result: [makerAddress] });
+ }
+ },
};
- const ecSignature = await ecSignOrderHashAsync(fakeProvider, orderHash, makerAddress, messagePrefixOpts);
- expect(ecSignature).to.deep.equal(expectedECSignature);
+
+ const ecSignature = await ecSignOrderHashAsync(fakeProvider, orderHash, makerAddress, SignerType.Metamask);
+ expect(ecSignature).to.equal(expectedSignature);
+ });
+ it('should return a valid signature', async () => {
+ const orderHash = '0x34decbedc118904df65f379a175bb39ca18209d6ce41d5ed549d54e6e0a95004';
+ const ecSignature = await ecSignOrderHashAsync(provider, orderHash, makerAddress, SignerType.Default);
+
+ const isValidSignature = await isValidSignatureAsync(provider, orderHash, ecSignature, makerAddress);
+ expect(isValidSignature).to.be.true();
+ });
+ });
+ describe('#convertECSignatureToSignatureHex', () => {
+ const ecSignature: ECSignature = {
+ v: 27,
+ r: '0xaca7da997ad177f040240cdccf6905b71ab16b74434388c3a72f34fd25d64393',
+ s: '0x46b2bac274ff29b48b3ea6e2d04c1336eaceafda3c53ab483fc3ff12fac3ebf2',
+ };
+ it('should concatenate v,r,s and append the Trezor signature type', async () => {
+ const expectedSignatureWithSignatureType =
+ '0x1baca7da997ad177f040240cdccf6905b71ab16b74434388c3a72f34fd25d6439346b2bac274ff29b48b3ea6e2d04c1336eaceafda3c53ab483fc3ff12fac3ebf208';
+ const signatureWithSignatureType = convertECSignatureToSignatureHex(ecSignature, SignerType.Trezor);
+ expect(signatureWithSignatureType).to.equal(expectedSignatureWithSignatureType);
+ });
+ it('should concatenate v,r,s and append the EthSign signature type when SignerType is Default', async () => {
+ const expectedSignatureWithSignatureType =
+ '0x1baca7da997ad177f040240cdccf6905b71ab16b74434388c3a72f34fd25d6439346b2bac274ff29b48b3ea6e2d04c1336eaceafda3c53ab483fc3ff12fac3ebf203';
+ const signatureWithSignatureType = convertECSignatureToSignatureHex(ecSignature, SignerType.Default);
+ expect(signatureWithSignatureType).to.equal(expectedSignatureWithSignatureType);
+ });
+ it('should concatenate v,r,s and append the EthSign signature type when SignerType is Ledger', async () => {
+ const expectedSignatureWithSignatureType =
+ '0x1baca7da997ad177f040240cdccf6905b71ab16b74434388c3a72f34fd25d6439346b2bac274ff29b48b3ea6e2d04c1336eaceafda3c53ab483fc3ff12fac3ebf203';
+ const signatureWithSignatureType = convertECSignatureToSignatureHex(ecSignature, SignerType.Ledger);
+ expect(signatureWithSignatureType).to.equal(expectedSignatureWithSignatureType);
+ });
+ it('should concatenate v,r,s and append the EthSign signature type when SignerType is Metamask', async () => {
+ const expectedSignatureWithSignatureType =
+ '0x1baca7da997ad177f040240cdccf6905b71ab16b74434388c3a72f34fd25d6439346b2bac274ff29b48b3ea6e2d04c1336eaceafda3c53ab483fc3ff12fac3ebf203';
+ const signatureWithSignatureType = convertECSignatureToSignatureHex(ecSignature, SignerType.Metamask);
+ expect(signatureWithSignatureType).to.equal(expectedSignatureWithSignatureType);
+ });
+ it('should throw if the SignerType is invalid', async () => {
+ const expectedMessage = 'Unrecognized SignerType: INVALID_SIGNER';
+ expect(() => convertECSignatureToSignatureHex(ecSignature, 'INVALID_SIGNER' as SignerType)).to.throw(
+ expectedMessage,
+ );
});
});
});
diff --git a/packages/subproviders/test/utils/fixture_data.ts b/packages/subproviders/test/utils/fixture_data.ts
index 3137e08b0..7cf502c97 100644
--- a/packages/subproviders/test/utils/fixture_data.ts
+++ b/packages/subproviders/test/utils/fixture_data.ts
@@ -25,7 +25,7 @@ export const fixtureData = {
chainId: networkId,
from: TEST_RPC_ACCOUNT_0,
},
- // This is the signed result of the abouve Transaction Data
+ // This is the signed result of the above Transaction Data
TX_DATA_SIGNED_RESULT:
'0xf85f8080822710940000000000000000000000000000000000000000808078a0712854c73c69445cc1b22a7c3d7312ff9a97fe4ffba35fd636e8236b211b6e7ca0647cee031615e52d916c7c707025bc64ad525d8f1b9876c3435a863b42743178',
TX_DATA_ACCOUNT_1_SIGNED_RESULT:
diff --git a/packages/types/CHANGELOG.json b/packages/types/CHANGELOG.json
index 8520c5146..9d78d3dc0 100644
--- a/packages/types/CHANGELOG.json
+++ b/packages/types/CHANGELOG.json
@@ -1,5 +1,14 @@
[
{
+ "version": "1.0.1-rc.4",
+ "changes": [
+ {
+ "pr": 914,
+ "note": "Added SignerType to handle different signing prefix scenarios"
+ }
+ ]
+ },
+ {
"version": "1.0.1-rc.3",
"changes": [
{
diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts
index 7774b61b0..fa634420d 100644
--- a/packages/types/src/index.ts
+++ b/packages/types/src/index.ts
@@ -143,6 +143,17 @@ export enum SignatureType {
}
/**
+ * The type of the Signer implementation. Some signer implementations use different message prefixes (e.g Trezor) or implement different
+ * eth_sign behaviour (e.g Metamask). Default assumes a spec compliant `eth_sign`.
+ */
+export enum SignerType {
+ Default = 'DEFAULT',
+ Ledger = 'LEDGER',
+ Metamask = 'METAMASK',
+ Trezor = 'TREZOR',
+}
+
+/**
* Elliptic Curve signature
*/
export interface ECSignature {