From fc6f98a63970b485042f8843ba7c7ca110b26ef4 Mon Sep 17 00:00:00 2001 From: Jean Regisser Date: Mon, 20 Mar 2023 17:16:02 +0100 Subject: [PATCH] Add all uint/int/bytes primitive types for EIP-712 signing (#10224) * Add all uint/int/bytes primitive types for EIP-712 signing * Implement zeroValue for all uint/int/bytes types * Typo * Put "bigger" types first --- .../utils/src/sign-typed-data-utils.test.ts | 163 ++++++++++++++++++ .../sdk/utils/src/sign-typed-data-utils.ts | 58 ++++--- 2 files changed, 197 insertions(+), 24 deletions(-) diff --git a/packages/sdk/utils/src/sign-typed-data-utils.test.ts b/packages/sdk/utils/src/sign-typed-data-utils.test.ts index b6785995380..62f788f27af 100644 --- a/packages/sdk/utils/src/sign-typed-data-utils.test.ts +++ b/packages/sdk/utils/src/sign-typed-data-utils.test.ts @@ -268,6 +268,169 @@ const TEST_TYPES: EIP712TestCase[] = [ }, ], }, + { + primaryType: 'AllAtomicTypes', + types: { + AllAtomicTypes: [ + { name: 'ui8', type: 'uint8' }, + { name: 'ui16', type: 'uint16' }, + { name: 'ui24', type: 'uint24' }, + { name: 'ui32', type: 'uint32' }, + { name: 'ui40', type: 'uint40' }, + { name: 'ui48', type: 'uint48' }, + { name: 'ui56', type: 'uint56' }, + { name: 'ui64', type: 'uint64' }, + { name: 'ui72', type: 'uint72' }, + { name: 'ui80', type: 'uint80' }, + { name: 'ui88', type: 'uint88' }, + { name: 'ui96', type: 'uint96' }, + { name: 'ui104', type: 'uint104' }, + { name: 'ui112', type: 'uint112' }, + { name: 'ui120', type: 'uint120' }, + { name: 'ui128', type: 'uint128' }, + { name: 'ui136', type: 'uint136' }, + { name: 'ui144', type: 'uint144' }, + { name: 'ui152', type: 'uint152' }, + { name: 'ui160', type: 'uint160' }, + { name: 'ui168', type: 'uint168' }, + { name: 'ui176', type: 'uint176' }, + { name: 'ui184', type: 'uint184' }, + { name: 'ui192', type: 'uint192' }, + { name: 'ui200', type: 'uint200' }, + { name: 'ui208', type: 'uint208' }, + { name: 'ui216', type: 'uint216' }, + { name: 'ui224', type: 'uint224' }, + { name: 'ui232', type: 'uint232' }, + { name: 'ui240', type: 'uint240' }, + { name: 'ui248', type: 'uint248' }, + { name: 'ui256', type: 'uint256' }, + { name: 'i8', type: 'int8' }, + { name: 'i16', type: 'int16' }, + { name: 'i24', type: 'int24' }, + { name: 'i32', type: 'int32' }, + { name: 'i40', type: 'int40' }, + { name: 'i48', type: 'int48' }, + { name: 'i56', type: 'int56' }, + { name: 'i64', type: 'int64' }, + { name: 'i72', type: 'int72' }, + { name: 'i80', type: 'int80' }, + { name: 'i88', type: 'int88' }, + { name: 'i96', type: 'int96' }, + { name: 'i104', type: 'int104' }, + { name: 'i112', type: 'int112' }, + { name: 'i120', type: 'int120' }, + { name: 'i128', type: 'int128' }, + { name: 'i136', type: 'int136' }, + { name: 'i144', type: 'int144' }, + { name: 'i152', type: 'int152' }, + { name: 'i160', type: 'int160' }, + { name: 'i168', type: 'int168' }, + { name: 'i176', type: 'int176' }, + { name: 'i184', type: 'int184' }, + { name: 'i192', type: 'int192' }, + { name: 'i200', type: 'int200' }, + { name: 'i208', type: 'int208' }, + { name: 'i216', type: 'int216' }, + { name: 'i224', type: 'int224' }, + { name: 'i232', type: 'int232' }, + { name: 'i240', type: 'int240' }, + { name: 'i248', type: 'int248' }, + { name: 'i256', type: 'int256' }, + { name: 'b1', type: 'bytes1' }, + { name: 'b2', type: 'bytes2' }, + { name: 'b3', type: 'bytes3' }, + { name: 'b4', type: 'bytes4' }, + { name: 'b5', type: 'bytes5' }, + { name: 'b6', type: 'bytes6' }, + { name: 'b7', type: 'bytes7' }, + { name: 'b8', type: 'bytes8' }, + { name: 'b9', type: 'bytes9' }, + { name: 'b10', type: 'bytes10' }, + { name: 'b11', type: 'bytes11' }, + { name: 'b12', type: 'bytes12' }, + { name: 'b13', type: 'bytes13' }, + { name: 'b14', type: 'bytes14' }, + { name: 'b15', type: 'bytes15' }, + { name: 'b16', type: 'bytes16' }, + { name: 'b17', type: 'bytes17' }, + { name: 'b18', type: 'bytes18' }, + { name: 'b19', type: 'bytes19' }, + { name: 'b20', type: 'bytes20' }, + { name: 'b21', type: 'bytes21' }, + { name: 'b22', type: 'bytes22' }, + { name: 'b23', type: 'bytes23' }, + { name: 'b24', type: 'bytes24' }, + { name: 'b25', type: 'bytes25' }, + { name: 'b26', type: 'bytes26' }, + { name: 'b27', type: 'bytes27' }, + { name: 'b28', type: 'bytes28' }, + { name: 'b29', type: 'bytes29' }, + { name: 'b30', type: 'bytes30' }, + { name: 'b31', type: 'bytes31' }, + { name: 'b32', type: 'bytes32' }, + { name: 'bl', type: 'bool' }, + { name: 'addr', type: 'address' }, + ], + }, + typeEncoding: + 'AllAtomicTypes(uint8 ui8,uint16 ui16,uint24 ui24,uint32 ui32,uint40 ui40,uint48 ui48,uint56 ui56,uint64 ui64,uint72 ui72,uint80 ui80,uint88 ui88,uint96 ui96,uint104 ui104,uint112 ui112,uint120 ui120,uint128 ui128,uint136 ui136,uint144 ui144,uint152 ui152,uint160 ui160,uint168 ui168,uint176 ui176,uint184 ui184,uint192 ui192,uint200 ui200,uint208 ui208,uint216 ui216,uint224 ui224,uint232 ui232,uint240 ui240,uint248 ui248,uint256 ui256,int8 i8,int16 i16,int24 i24,int32 i32,int40 i40,int48 i48,int56 i56,int64 i64,int72 i72,int80 i80,int88 i88,int96 i96,int104 i104,int112 i112,int120 i120,int128 i128,int136 i136,int144 i144,int152 i152,int160 i160,int168 i168,int176 i176,int184 i184,int192 i192,int200 i200,int208 i208,int216 i216,int224 i224,int232 i232,int240 i240,int248 i248,int256 i256,bytes1 b1,bytes2 b2,bytes3 b3,bytes4 b4,bytes5 b5,bytes6 b6,bytes7 b7,bytes8 b8,bytes9 b9,bytes10 b10,bytes11 b11,bytes12 b12,bytes13 b13,bytes14 b14,bytes15 b15,bytes16 b16,bytes17 b17,bytes18 b18,bytes19 b19,bytes20 b20,bytes21 b21,bytes22 b22,bytes23 b23,bytes24 b24,bytes25 b25,bytes26 b26,bytes27 b27,bytes28 b28,bytes29 b29,bytes30 b30,bytes31 b31,bytes32 b32,bool bl,address addr)', + examples: [], + }, + { + primaryType: 'SomeAtomicTypes', + types: { + SomeAtomicTypes: [ + { name: 'ui8', type: 'uint8' }, + { name: 'ui160', type: 'uint160' }, + { name: 'ui256', type: 'uint256' }, + { name: 'i8', type: 'int8' }, + { name: 'i160', type: 'int160' }, + { name: 'i256', type: 'int256' }, + { name: 'b1', type: 'bytes1' }, + { name: 'b16', type: 'bytes16' }, + { name: 'b32', type: 'bytes32' }, + ], + }, + typeEncoding: + 'SomeAtomicTypes(uint8 ui8,uint160 ui160,uint256 ui256,int8 i8,int160 i160,int256 i256,bytes1 b1,bytes16 b16,bytes32 b32)', + zero: { + ui8: 0, + ui160: 0, + ui256: 0, + i8: 0, + i160: 0, + i256: 0, + b1: Buffer.from([]), + b16: Buffer.from([]), + b32: Buffer.from([]), + }, + examples: [ + { + data: { + ui8: 250, + ui160: '0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b', + ui256: '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + i8: -120, + i160: '0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b', + i256: '0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + b1: '0x01', + b16: '0x0102030405060708090a0b0c0d0e0f10', + b32: '0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20', + }, + dataEncoding: Buffer.concat([ + Buffer.from('00000000000000000000000000000000000000000000000000000000000000fa', 'hex'), + Buffer.from('0000000000000000000000001a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b', 'hex'), + Buffer.from('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 'hex'), + Buffer.from('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff88', 'hex'), + Buffer.from('0000000000000000000000001a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b', 'hex'), + Buffer.from('7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 'hex'), + Buffer.from('0100000000000000000000000000000000000000000000000000000000000000', 'hex'), + Buffer.from('0102030405060708090a0b0c0d0e0f1000000000000000000000000000000000', 'hex'), + Buffer.from('0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20', 'hex'), + ]), + }, + ], + }, ] describe('encodeType()', () => { diff --git a/packages/sdk/utils/src/sign-typed-data-utils.ts b/packages/sdk/utils/src/sign-typed-data-utils.ts index 52c155543b4..bc25cd16106 100644 --- a/packages/sdk/utils/src/sign-typed-data-utils.ts +++ b/packages/sdk/utils/src/sign-typed-data-utils.ts @@ -40,28 +40,36 @@ export interface EIP712TypedData { /** Array of all EIP-712 atomic type names. */ export const EIP712_ATOMIC_TYPES = [ - 'bytes1', - 'bytes32', - 'uint8', - 'uint64', - 'uint256', - // This list should technically include all types from uint8 to uint256, and int8 to int256 - 'int8', - 'int256', 'bool', 'address', + // bytes types from 1 to 32 bytes + // and uint/int types from 8 to 256 bits + ...(() => { + const result = [] + // Putting "bigger" types first, assuming they are more likely to be used. + // So `EIP712_ATOMIC_TYPES.includes(...)` calls are faster. (likely useless micro-optimization :D) + for (let i = 32; i >= 1; i--) { + result.push('bytes' + i) + result.push('uint' + i * 8) + result.push('int' + i * 8) + } + return result + })(), ] export const EIP712_DYNAMIC_TYPES = ['bytes', 'string'] -export const EIP712_BUILTIN_TYPES = EIP712_ATOMIC_TYPES.concat(EIP712_DYNAMIC_TYPES) +export const EIP712_BUILTIN_TYPES = EIP712_DYNAMIC_TYPES.concat(EIP712_ATOMIC_TYPES) // Regular expression used to identify and parse EIP-712 array type strings. const EIP712_ARRAY_REGEXP = /^(?[\w<>\[\]_\-]+)(\[(?\d+)?\])$/ -// Regular experssion used to identity EIP-712 integer types (e.g. int256, uint256, uint8). +// Regular expression used to identify EIP-712 integer types (e.g. int256, uint256, uint8). const EIP712_INT_REGEXP = /^u?int\d*$/ +// Regular expression used to identify EIP-712 bytes types (e.g. bytes, bytes1, up to bytes32). +const EIP712_BYTES_REGEXP = /^bytes\d*$/ + /** * Utility type representing an optional value in a EIP-712 compatible manner, as long as the * concrete type T is a subtype of EIP712ObjectValue. @@ -265,20 +273,22 @@ export function structHash(primaryType: string, data: EIP712Object, types: EIP71 */ export function zeroValue(primaryType: string, types: EIP712Types = {}): EIP712ObjectValue { // If the type is a built-in, return a pre-defined zero value. - if (['bytes', 'bytes1', 'bytes32'].includes(primaryType)) { - return Buffer.alloc(0) - } - if (['uint8', 'uint256', 'int8', 'int256'].includes(primaryType)) { - return 0 - } - if (primaryType === 'bool') { - return false - } - if (primaryType === 'address') { - return NULL_ADDRESS - } - if (primaryType === 'string') { - return '' + if (EIP712_BUILTIN_TYPES.includes(primaryType)) { + if (EIP712_BYTES_REGEXP.test(primaryType)) { + return Buffer.alloc(0) + } + if (EIP712_INT_REGEXP.test(primaryType)) { + return 0 + } + if (primaryType === 'bool') { + return false + } + if (primaryType === 'address') { + return NULL_ADDRESS + } + if (primaryType === 'string') { + return '' + } } // If the type is an array, return an empty array or an array of the given fixed length.