aboutsummaryrefslogtreecommitdiffstats
path: root/packages/utils
diff options
context:
space:
mode:
authorJacob Evans <jacob@dekz.net>2018-10-04 15:12:35 +0800
committerJacob Evans <jacob@dekz.net>2018-10-05 10:00:41 +0800
commit2a82ff48c061eacb3b6f9fb36eeae7f515b6d11d (patch)
tree9e6658db449425bcb7c4e4bbee4a328525dc1902 /packages/utils
parent07926ded6ef194969ffe26e3879d6e86a0eb9c50 (diff)
downloaddexon-0x-contracts-2a82ff48c061eacb3b6f9fb36eeae7f515b6d11d.tar
dexon-0x-contracts-2a82ff48c061eacb3b6f9fb36eeae7f515b6d11d.tar.gz
dexon-0x-contracts-2a82ff48c061eacb3b6f9fb36eeae7f515b6d11d.tar.bz2
dexon-0x-contracts-2a82ff48c061eacb3b6f9fb36eeae7f515b6d11d.tar.lz
dexon-0x-contracts-2a82ff48c061eacb3b6f9fb36eeae7f515b6d11d.tar.xz
dexon-0x-contracts-2a82ff48c061eacb3b6f9fb36eeae7f515b6d11d.tar.zst
dexon-0x-contracts-2a82ff48c061eacb3b6f9fb36eeae7f515b6d11d.zip
Move SignTypedData to utils package
Diffstat (limited to 'packages/utils')
-rw-r--r--packages/utils/src/index.ts1
-rw-r--r--packages/utils/src/sign_typed_data_utils.ts81
-rw-r--r--packages/utils/test/sign_typed_data_utils_test.ts107
3 files changed, 189 insertions, 0 deletions
diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts
index 9d01e5bc5..0723e5788 100644
--- a/packages/utils/src/index.ts
+++ b/packages/utils/src/index.ts
@@ -9,3 +9,4 @@ export { abiUtils } from './abi_utils';
export { NULL_BYTES } from './constants';
export { errorUtils } from './error_utils';
export { fetchAsync } from './fetch_async';
+export { signTypedDataUtils } from './sign_typed_data_utils';
diff --git a/packages/utils/src/sign_typed_data_utils.ts b/packages/utils/src/sign_typed_data_utils.ts
new file mode 100644
index 000000000..902d8530c
--- /dev/null
+++ b/packages/utils/src/sign_typed_data_utils.ts
@@ -0,0 +1,81 @@
+import * as ethUtil from 'ethereumjs-util';
+import * as ethers from 'ethers';
+
+export interface EIP712Parameter {
+ name: string;
+ type: string;
+}
+
+export interface EIP712Types {
+ [key: string]: EIP712Parameter[];
+}
+export interface EIP712TypedData {
+ types: EIP712Types;
+ domain: any;
+ message: any;
+ primaryType: string;
+}
+
+export const signTypedDataUtils = {
+ findDependencies(primaryType: string, types: EIP712Types, found: string[] = []): string[] {
+ if (found.includes(primaryType) || types[primaryType] === undefined) {
+ return found;
+ }
+ found.push(primaryType);
+ for (const field of types[primaryType]) {
+ for (const dep of signTypedDataUtils.findDependencies(field.type, types, found)) {
+ if (!found.includes(dep)) {
+ found.push(dep);
+ }
+ }
+ }
+ return found;
+ },
+ encodeType(primaryType: string, types: EIP712Types): string {
+ let deps = signTypedDataUtils.findDependencies(primaryType, types);
+ deps = deps.filter(d => d !== primaryType);
+ deps = [primaryType].concat(deps.sort());
+ let result = '';
+ for (const dep of deps) {
+ result += `${dep}(${types[dep].map(({ name, type }) => `${type} ${name}`).join(',')})`;
+ }
+ return result;
+ },
+ encodeData(primaryType: string, data: any, types: EIP712Types): string {
+ const encodedTypes = ['bytes32'];
+ const encodedValues = [signTypedDataUtils.typeHash(primaryType, types)];
+ for (const field of types[primaryType]) {
+ let value = data[field.name];
+ if (field.type === 'string' || field.type === 'bytes') {
+ value = ethUtil.sha3(value);
+ encodedTypes.push('bytes32');
+ encodedValues.push(value);
+ } else if (types[field.type] !== undefined) {
+ encodedTypes.push('bytes32');
+ value = ethUtil.sha3(signTypedDataUtils.encodeData(field.type, value, types));
+ encodedValues.push(value);
+ } else if (field.type.lastIndexOf(']') === field.type.length - 1) {
+ throw new Error('Arrays currently unimplemented in encodeData');
+ } else {
+ encodedTypes.push(field.type);
+ encodedValues.push(value);
+ }
+ }
+ return ethers.utils.defaultAbiCoder.encode(encodedTypes, encodedValues);
+ },
+ typeHash(primaryType: string, types: EIP712Types): Buffer {
+ return ethUtil.sha3(signTypedDataUtils.encodeType(primaryType, types));
+ },
+ structHash(primaryType: string, data: any, types: EIP712Types): Buffer {
+ return ethUtil.sha3(signTypedDataUtils.encodeData(primaryType, data, types));
+ },
+ signTypedDataHash(typedData: EIP712TypedData): Buffer {
+ return ethUtil.sha3(
+ Buffer.concat([
+ Buffer.from('1901', 'hex'),
+ signTypedDataUtils.structHash('EIP712Domain', typedData.domain, typedData.types),
+ signTypedDataUtils.structHash(typedData.primaryType, typedData.message, typedData.types),
+ ]),
+ );
+ },
+};
diff --git a/packages/utils/test/sign_typed_data_utils_test.ts b/packages/utils/test/sign_typed_data_utils_test.ts
new file mode 100644
index 000000000..b21ffefa0
--- /dev/null
+++ b/packages/utils/test/sign_typed_data_utils_test.ts
@@ -0,0 +1,107 @@
+import * as chai from 'chai';
+import 'mocha';
+
+import { signTypedDataUtils } from '../src/sign_typed_data_utils';
+
+const expect = chai.expect;
+
+describe('signTypedDataUtils', () => {
+ describe('signTypedDataHash', () => {
+ const signTypedDataHashHex = '0x55eaa6ec02f3224d30873577e9ddd069a288c16d6fb407210eecbc501fa76692';
+ const signTypedData = {
+ types: {
+ EIP712Domain: [
+ {
+ name: 'name',
+ type: 'string',
+ },
+ {
+ name: 'version',
+ type: 'string',
+ },
+ {
+ name: 'verifyingContract',
+ type: 'address',
+ },
+ ],
+ Order: [
+ {
+ name: 'makerAddress',
+ type: 'address',
+ },
+ {
+ name: 'takerAddress',
+ type: 'address',
+ },
+ {
+ name: 'feeRecipientAddress',
+ type: 'address',
+ },
+ {
+ name: 'senderAddress',
+ type: 'address',
+ },
+ {
+ name: 'makerAssetAmount',
+ type: 'uint256',
+ },
+ {
+ name: 'takerAssetAmount',
+ type: 'uint256',
+ },
+ {
+ name: 'makerFee',
+ type: 'uint256',
+ },
+ {
+ name: 'takerFee',
+ type: 'uint256',
+ },
+ {
+ name: 'expirationTimeSeconds',
+ type: 'uint256',
+ },
+ {
+ name: 'salt',
+ type: 'uint256',
+ },
+ {
+ name: 'makerAssetData',
+ type: 'bytes',
+ },
+ {
+ name: 'takerAssetData',
+ type: 'bytes',
+ },
+ ],
+ },
+ domain: {
+ name: '0x Protocol',
+ version: '2',
+ verifyingContract: '0x0000000000000000000000000000000000000000',
+ },
+ message: {
+ makerAddress: '0x0000000000000000000000000000000000000000',
+ takerAddress: '0x0000000000000000000000000000000000000000',
+ makerAssetAmount: '1000000000000000000',
+ takerAssetAmount: '1000000000000000000',
+ expirationTimeSeconds: '12345',
+ makerFee: '0',
+ takerFee: '0',
+ feeRecipientAddress: '0x0000000000000000000000000000000000000000',
+ senderAddress: '0x0000000000000000000000000000000000000000',
+ salt: '12345',
+ makerAssetData: '0x0000000000000000000000000000000000000000',
+ takerAssetData: '0x0000000000000000000000000000000000000000',
+ exchangeAddress: '0x0000000000000000000000000000000000000000',
+ },
+ primaryType: 'Order',
+ };
+ it.only('creates a known hash of the sign typed data', () => {
+ const hash = signTypedDataUtils.signTypedDataHash(signTypedData).toString('hex');
+ const hashHex = `0x${hash}`;
+ expect(hashHex).to.be.eq(signTypedDataHashHex);
+ console.log(hash);
+ });
+ });
+});