aboutsummaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
authorJacob Evans <jacob@dekz.net>2018-10-01 18:37:13 +0800
committerJacob Evans <jacob@dekz.net>2018-10-05 09:59:24 +0800
commitadcfaa2e80389f69e196b602955cee858a1eb40f (patch)
treed97a4c65982f5062ef70d357b6bad1181dec0292 /packages
parent119f8c94495a15ef43298a77abc87fb9d33b4bb3 (diff)
downloaddexon-sol-tools-adcfaa2e80389f69e196b602955cee858a1eb40f.tar
dexon-sol-tools-adcfaa2e80389f69e196b602955cee858a1eb40f.tar.gz
dexon-sol-tools-adcfaa2e80389f69e196b602955cee858a1eb40f.tar.bz2
dexon-sol-tools-adcfaa2e80389f69e196b602955cee858a1eb40f.tar.lz
dexon-sol-tools-adcfaa2e80389f69e196b602955cee858a1eb40f.tar.xz
dexon-sol-tools-adcfaa2e80389f69e196b602955cee858a1eb40f.tar.zst
dexon-sol-tools-adcfaa2e80389f69e196b602955cee858a1eb40f.zip
Expose eth_signTypedData functionality for order signing
Diffstat (limited to 'packages')
-rw-r--r--packages/json-schemas/schemas/eip712_typed_data.ts28
-rw-r--r--packages/json-schemas/src/schemas.ts2
-rw-r--r--packages/order-utils/src/eip712_utils.ts6
-rw-r--r--packages/order-utils/src/order_hash.ts2
-rw-r--r--packages/order-utils/src/signature_utils.ts50
-rw-r--r--packages/order-utils/test/signature_utils_test.ts55
-rw-r--r--packages/subproviders/package.json2
-rw-r--r--packages/web3-wrapper/src/web3_wrapper.ts15
8 files changed, 144 insertions, 16 deletions
diff --git a/packages/json-schemas/schemas/eip712_typed_data.ts b/packages/json-schemas/schemas/eip712_typed_data.ts
new file mode 100644
index 000000000..4c4664878
--- /dev/null
+++ b/packages/json-schemas/schemas/eip712_typed_data.ts
@@ -0,0 +1,28 @@
+export const eip712TypedData = {
+ id: 'eip712TypedData',
+ type: 'object',
+ properties: {
+ types: {
+ type: 'object',
+ properties: {
+ EIP712Domain: { type: 'array' },
+ },
+ additionalProperties: {
+ type: 'array',
+ items: {
+ type: 'object',
+ properties: {
+ name: { type: 'string' },
+ type: { type: 'string' },
+ },
+ required: ['name', 'type'],
+ },
+ },
+ required: ['EIP712Domain'],
+ },
+ primaryType: { type: 'string' },
+ domain: { type: 'object' },
+ message: { type: 'object' },
+ },
+ required: ['types', 'primaryType', 'domain', 'message'],
+};
diff --git a/packages/json-schemas/src/schemas.ts b/packages/json-schemas/src/schemas.ts
index 3bc37f96b..50dc8d091 100644
--- a/packages/json-schemas/src/schemas.ts
+++ b/packages/json-schemas/src/schemas.ts
@@ -2,6 +2,7 @@ import { addressSchema, hexSchema, numberSchema } from '../schemas/basic_type_sc
import { blockParamSchema, blockRangeSchema } from '../schemas/block_range_schema';
import { callDataSchema } from '../schemas/call_data_schema';
import { ecSignatureParameterSchema, ecSignatureSchema } from '../schemas/ec_signature_schema';
+import { eip712TypedData } from '../schemas/eip712_typed_data';
import { indexFilterValuesSchema } from '../schemas/index_filter_values_schema';
import { orderCancellationRequestsSchema } from '../schemas/order_cancel_schema';
import { orderFillOrKillRequestsSchema } from '../schemas/order_fill_or_kill_requests_schema';
@@ -39,6 +40,7 @@ export const schemas = {
hexSchema,
ecSignatureParameterSchema,
ecSignatureSchema,
+ eip712TypedData,
indexFilterValuesSchema,
orderCancellationRequestsSchema,
orderFillOrKillRequestsSchema,
diff --git a/packages/order-utils/src/eip712_utils.ts b/packages/order-utils/src/eip712_utils.ts
index b303c93dc..01f1e930e 100644
--- a/packages/order-utils/src/eip712_utils.ts
+++ b/packages/order-utils/src/eip712_utils.ts
@@ -5,11 +5,11 @@ import { crypto } from './crypto';
import { EIP712Schema, EIP712Types } from './types';
const EIP191_PREFIX = '\x19\x01';
-const EIP712_DOMAIN_NAME = '0x Protocol';
-const EIP712_DOMAIN_VERSION = '2';
const EIP712_VALUE_LENGTH = 32;
+export const EIP712_DOMAIN_NAME = '0x Protocol';
+export const EIP712_DOMAIN_VERSION = '2';
-const EIP712_DOMAIN_SCHEMA: EIP712Schema = {
+export const EIP712_DOMAIN_SCHEMA: EIP712Schema = {
name: 'EIP712Domain',
parameters: [
{ name: 'name', type: EIP712Types.String },
diff --git a/packages/order-utils/src/order_hash.ts b/packages/order-utils/src/order_hash.ts
index 8e98f8767..151c1f801 100644
--- a/packages/order-utils/src/order_hash.ts
+++ b/packages/order-utils/src/order_hash.ts
@@ -8,7 +8,7 @@ import { EIP712Schema, EIP712Types } from './types';
const INVALID_TAKER_FORMAT = 'instance.takerAddress is not of a type(s) string';
-const EIP712_ORDER_SCHEMA: EIP712Schema = {
+export const EIP712_ORDER_SCHEMA: EIP712Schema = {
name: 'Order',
parameters: [
{ name: 'makerAddress', type: EIP712Types.Address },
diff --git a/packages/order-utils/src/signature_utils.ts b/packages/order-utils/src/signature_utils.ts
index 3b656d3fc..05c673ae2 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, SignerType, ValidatorSignature } from '@0xproject/types';
+import { ECSignature, Order, SignatureType, SignerType, ValidatorSignature } from '@0xproject/types';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { Provider } from 'ethereum-types';
import * as ethUtil from 'ethereumjs-util';
@@ -7,9 +7,11 @@ import * as _ from 'lodash';
import { artifacts } from './artifacts';
import { assert } from './assert';
+import { EIP712_DOMAIN_NAME, EIP712_DOMAIN_SCHEMA, EIP712_DOMAIN_VERSION } from './eip712_utils';
import { ExchangeContract } from './generated_contract_wrappers/exchange';
import { IValidatorContract } from './generated_contract_wrappers/i_validator';
import { IWalletContract } from './generated_contract_wrappers/i_wallet';
+import { EIP712_ORDER_SCHEMA } from './order_hash';
import { OrderError } from './types';
import { utils } from './utils';
@@ -192,6 +194,52 @@ export const signatureUtils = {
}
},
/**
+ * Signs an order using `eth_signTypedData` and returns it's elliptic curve signature and signature type.
+ * This method currently supports Ganache.
+ * @param order The Order 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.
+ * @return A hex encoded string containing the Elliptic curve signature generated by signing the orderHash and the Signature Type.
+ */
+ async ecSignOrderAsync(provider: Provider, order: Order, signerAddress: string): Promise<string> {
+ assert.isWeb3Provider('provider', provider);
+ assert.isETHAddressHex('signerAddress', signerAddress);
+ const web3Wrapper = new Web3Wrapper(provider);
+ await assert.isSenderAddressAsync('signerAddress', signerAddress, web3Wrapper);
+ const normalizedSignerAddress = signerAddress.toLowerCase();
+ const typedData = {
+ types: {
+ EIP712Domain: EIP712_DOMAIN_SCHEMA.parameters,
+ Order: EIP712_ORDER_SCHEMA.parameters,
+ },
+ domain: {
+ name: EIP712_DOMAIN_NAME,
+ version: EIP712_DOMAIN_VERSION,
+ verifyingContract: order.exchangeAddress,
+ },
+ message: {
+ ...order,
+ salt: order.salt.toString(),
+ makerFee: order.makerFee.toString(),
+ takerFee: order.takerFee.toString(),
+ makerAssetAmount: order.makerAssetAmount.toString(),
+ takerAssetAmount: order.takerAssetAmount.toString(),
+ expirationTimeSeconds: order.expirationTimeSeconds.toString(),
+ },
+ primaryType: 'Order',
+ };
+ const signature = await web3Wrapper.signTypedDataAsync(normalizedSignerAddress, typedData);
+ const ecSignatureRSV = parseSignatureHexAsRSV(signature);
+ const signatureBuffer = Buffer.concat([
+ ethUtil.toBuffer(ecSignatureRSV.v),
+ ethUtil.toBuffer(ecSignatureRSV.r),
+ ethUtil.toBuffer(ecSignatureRSV.s),
+ ethUtil.toBuffer(SignatureType.EIP712),
+ ]);
+ const signatureHex = `0x${signatureBuffer.toString('hex')}`;
+ return signatureHex;
+ },
+ /**
* 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.
diff --git a/packages/order-utils/test/signature_utils_test.ts b/packages/order-utils/test/signature_utils_test.ts
index 2ca1109a1..40ce16165 100644
--- a/packages/order-utils/test/signature_utils_test.ts
+++ b/packages/order-utils/test/signature_utils_test.ts
@@ -1,12 +1,13 @@
-import { SignerType } from '@0xproject/types';
+import { Order, SignatureType, SignerType } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import * as chai from 'chai';
import { JSONRPCErrorCallback, JSONRPCRequestPayload } from 'ethereum-types';
+import * as ethUtil from 'ethereumjs-util';
import * as _ from 'lodash';
import 'mocha';
-import * as Sinon from 'sinon';
-import { generatePseudoRandomSalt } from '../src';
+import { generatePseudoRandomSalt, orderHashUtils } from '../src';
+import { constants } from '../src/constants';
import { signatureUtils } from '../src/signature_utils';
import { chaiSetup } from './utils/chai_setup';
@@ -115,19 +116,53 @@ describe('Signature utils', () => {
expect(salt.lessThan(twoPow256)).to.be.true();
});
});
- describe('#ecSignOrderHashAsync', () => {
- let stubs: Sinon.SinonStub[] = [];
+ describe('#ecSignOrderAsync', () => {
let makerAddress: string;
+ const fakeExchangeContractAddress = '0x1dc4c1cefef38a777b15aa20260a54e584b16c48';
+ let order: Order;
before(async () => {
const availableAddreses = await web3Wrapper.getAvailableAddressesAsync();
makerAddress = availableAddreses[0];
+ order = {
+ makerAddress,
+ takerAddress: constants.NULL_ADDRESS,
+ senderAddress: constants.NULL_ADDRESS,
+ feeRecipientAddress: constants.NULL_ADDRESS,
+ makerAssetData: constants.NULL_ADDRESS,
+ takerAssetData: constants.NULL_ADDRESS,
+ exchangeAddress: fakeExchangeContractAddress,
+ salt: new BigNumber(0),
+ makerFee: new BigNumber(0),
+ takerFee: new BigNumber(0),
+ makerAssetAmount: new BigNumber(0),
+ takerAssetAmount: new BigNumber(0),
+ expirationTimeSeconds: new BigNumber(0),
+ };
});
- afterEach(() => {
- // clean up any stubs after the test has completed
- _.each(stubs, s => s.restore());
- stubs = [];
+ it('should result in the same signature as signing order hash without prefix', async () => {
+ const orderHashHex = orderHashUtils.getOrderHashHex(order);
+ const sig = ethUtil.ecsign(
+ ethUtil.toBuffer(orderHashHex),
+ Buffer.from('F2F48EE19680706196E2E339E5DA3491186E0C4C5030670656B0E0164837257D', 'hex'),
+ );
+ const signatureBuffer = Buffer.concat([
+ ethUtil.toBuffer(sig.v),
+ ethUtil.toBuffer(sig.r),
+ ethUtil.toBuffer(sig.s),
+ ethUtil.toBuffer(SignatureType.EIP712),
+ ]);
+ const signatureHex = `0x${signatureBuffer.toString('hex')}`;
+ const eip712Signature = await signatureUtils.ecSignOrderAsync(provider, order, makerAddress);
+ expect(signatureHex).to.eq(eip712Signature);
+ });
+ });
+ describe('#ecSignOrderHashAsync', () => {
+ let makerAddress: string;
+ before(async () => {
+ const availableAddreses = await web3Wrapper.getAvailableAddressesAsync();
+ makerAddress = availableAddreses[0];
});
- it('Should return the correct Signature', async () => {
+ it('should return the correct Signature', async () => {
const orderHash = '0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0';
const expectedSignature =
'0x1b61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc3340349190569279751135161d22529dc25add4f6069af05be04cacbda2ace225403';
diff --git a/packages/subproviders/package.json b/packages/subproviders/package.json
index fad478349..ff3ab7ed5 100644
--- a/packages/subproviders/package.json
+++ b/packages/subproviders/package.json
@@ -45,7 +45,7 @@
"ethereum-types": "^1.0.11",
"ethereumjs-tx": "^1.3.5",
"ethereumjs-util": "^5.1.1",
- "ganache-core": "0xProject/ganache-core#monorepo-dep",
+ "ganache-core": "^2.2.1",
"hdkey": "^0.7.1",
"json-rpc-error": "2.0.0",
"lodash": "^4.17.5",
diff --git a/packages/web3-wrapper/src/web3_wrapper.ts b/packages/web3-wrapper/src/web3_wrapper.ts
index d52c1cb6e..df6879a48 100644
--- a/packages/web3-wrapper/src/web3_wrapper.ts
+++ b/packages/web3-wrapper/src/web3_wrapper.ts
@@ -315,6 +315,21 @@ export class Web3Wrapper {
return signData;
}
/**
+ * Sign an EIP712 typed data message with a specific address's private key (`eth_signTypedData`)
+ * @param address Address of signer
+ * @param typedData Typed data message to sign
+ * @returns Signature string (might be VRS or RSV depending on the Signer)
+ */
+ public async signTypedDataAsync(address: string, typedData: object): Promise<string> {
+ assert.isETHAddressHex('address', address);
+ assert.doesConformToSchema('typedData', typedData, schemas.eip712TypedData);
+ const signData = await this.sendRawPayloadAsync<string>({
+ method: 'eth_signTypedData',
+ params: [address, typedData],
+ });
+ return signData;
+ }
+ /**
* Fetches the latest block number
* @returns Block number
*/