aboutsummaryrefslogtreecommitdiffstats
path: root/packages/utils/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/utils/src')
-rw-r--r--packages/utils/src/index.ts1
-rw-r--r--packages/utils/src/sign_typed_data_utils.ts81
2 files changed, 82 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),
+ ]),
+ );
+ },
+};