diff options
Diffstat (limited to 'packages')
11 files changed, 305 insertions, 107 deletions
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinTransactions.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinTransactions.sol index 30b0102fd..f1332363c 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinTransactions.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinTransactions.sol @@ -20,8 +20,11 @@ pragma solidity ^0.4.24; import "./libs/LibExchangeErrors.sol"; import "./mixins/MSignatureValidator.sol"; import "./mixins/MTransactions.sol"; +import "./libs/LibExchangeErrors.sol"; +import "./libs/LibEIP712.sol"; contract MixinTransactions is + LibEIP712, LibExchangeErrors, MSignatureValidator, MTransactions @@ -34,6 +37,33 @@ contract MixinTransactions is // Address of current transaction signer address public currentContextAddress; + // Hash for the EIP712 ZeroEx Transaction Schema + bytes32 constant EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH = keccak256(abi.encodePacked( + "ZeroExTransaction(", + "uint256 salt,", + "address signer,", + "bytes data", + ")" + )); + + /// @dev Calculates EIP712 hash of the Transaction. + /// @param salt Arbitrary number to ensure uniqueness of transaction hash. + /// @param signer Address of transaction signer. + /// @param data AbiV2 encoded calldata. + /// @return EIP712 hash of the Transaction. + function hashZeroExTransaction(uint256 salt, address signer, bytes data) + internal + pure + returns (bytes32) + { + return keccak256(abi.encode( + EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH, + salt, + signer, + keccak256(data) + )); + } + /// @dev Executes an exchange method call in the context of signer. /// @param salt Arbitrary number to ensure uniqueness of transaction hash. /// @param signer Address of transaction signer. @@ -53,13 +83,7 @@ contract MixinTransactions is REENTRANCY_ILLEGAL ); - // Calculate transaction hash - bytes32 transactionHash = keccak256(abi.encodePacked( - address(this), - signer, - salt, - data - )); + bytes32 transactionHash = hashEIP712Message(hashZeroExTransaction(salt, signer, data)); // Validate transaction has not been executed require( diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibEIP712.sol b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibEIP712.sol new file mode 100644 index 000000000..b983347a4 --- /dev/null +++ b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibEIP712.sol @@ -0,0 +1,64 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.4.24; + +contract LibEIP712 { + // EIP191 header for EIP712 prefix + string constant EIP191_HEADER = "\x19\x01"; + + // EIP712 Domain Name value + string constant EIP712_DOMAIN_NAME = "0x Protocol"; + + // EIP712 Domain Version value + string constant EIP712_DOMAIN_VERSION = "2"; + + // Hash of the EIP712 Domain Separator Schema + bytes32 public constant EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256(abi.encodePacked( + "EIP712Domain(", + "string name,", + "string version,", + "address verifyingContract", + ")" + )); + + // Hash of the EIP712 Domain Separator data + bytes32 public EIP712_DOMAIN_HASH; + + constructor () + public + { + EIP712_DOMAIN_HASH = keccak256(abi.encode( + EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH, + keccak256(bytes(EIP712_DOMAIN_NAME)), + keccak256(bytes(EIP712_DOMAIN_VERSION)), + address(this) + )); + } + + /// @dev Calculates EIP712 encoding for a hash struct in this EIP712 Domain. + /// @param hashStruct The EIP712 hash struct. + /// @return EIP712 hash applied to this EIP712 Domain. + function hashEIP712Message(bytes32 hashStruct) + internal + view + returns (bytes32) + { + return keccak256(abi.encodePacked(EIP191_HEADER, EIP712_DOMAIN_HASH, hashStruct)); + } +} diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol index 05ea27ffc..bfc7aaae0 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibOrder.sol @@ -18,13 +18,14 @@ pragma solidity ^0.4.24; -contract LibOrder { +import "./LibEIP712.sol"; - bytes32 constant DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256(abi.encodePacked( - "DomainSeparator(address contract)" - )); +contract LibOrder is + LibEIP712 +{ - bytes32 constant ORDER_SCHEMA_HASH = keccak256(abi.encodePacked( + // Hash for the EIP712 Order Schema + bytes32 constant EIP712_ORDER_SCHEMA_HASH = keccak256(abi.encodePacked( "Order(", "address makerAddress,", "address takerAddress,", @@ -37,7 +38,7 @@ contract LibOrder { "uint256 expirationTimeSeconds,", "uint256 salt,", "bytes makerAssetData,", - "bytes takerAssetData,", + "bytes takerAssetData", ")" )); @@ -85,27 +86,38 @@ contract LibOrder { view returns (bytes32 orderHash) { - // TODO: EIP712 is not finalized yet - // Source: https://github.com/ethereum/EIPs/pull/712 - orderHash = keccak256(abi.encodePacked( - DOMAIN_SEPARATOR_SCHEMA_HASH, - keccak256(abi.encodePacked(address(this))), - ORDER_SCHEMA_HASH, - keccak256(abi.encodePacked( - order.makerAddress, - order.takerAddress, - order.feeRecipientAddress, - order.senderAddress, - order.makerAssetAmount, - order.takerAssetAmount, - order.makerFee, - order.takerFee, - order.expirationTimeSeconds, - order.salt, - keccak256(abi.encodePacked(order.makerAssetData)), - keccak256(abi.encodePacked(order.takerAssetData)) - )) - )); + orderHash = hashEIP712Message(hashOrder(order)); return orderHash; } + + /// @dev Calculates EIP712 hash of the order. + /// @param order The order structure. + /// @return EIP712 hash of the order. + function hashOrder(Order memory order) + internal + pure + returns (bytes32 result) + { + bytes32 schemaHash = EIP712_ORDER_SCHEMA_HASH; + bytes32 makerAssetDataHash = keccak256(order.makerAssetData); + bytes32 takerAssetDataHash = keccak256(order.takerAssetData); + assembly { + // Backup + let temp1 := mload(sub(order, 32)) + let temp2 := mload(add(order, 320)) + let temp3 := mload(add(order, 352)) + + // Hash in place + mstore(sub(order, 32), schemaHash) + mstore(add(order, 320), makerAssetDataHash) + mstore(add(order, 352), takerAssetDataHash) + result := keccak256(sub(order, 32), 416) + + // Restore + mstore(sub(order, 32), temp1) + mstore(add(order, 320), temp2) + mstore(add(order, 352), temp3) + } + return result; + } } diff --git a/packages/contracts/src/contracts/current/test/TestLibs/TestLibs.sol b/packages/contracts/src/contracts/current/test/TestLibs/TestLibs.sol index 47ce0dcf3..010080703 100644 --- a/packages/contracts/src/contracts/current/test/TestLibs/TestLibs.sol +++ b/packages/contracts/src/contracts/current/test/TestLibs/TestLibs.sol @@ -74,7 +74,7 @@ contract TestLibs is pure returns (bytes32) { - return ORDER_SCHEMA_HASH; + return EIP712_ORDER_SCHEMA_HASH; } function getDomainSeparatorSchemaHash() @@ -82,7 +82,7 @@ contract TestLibs is pure returns (bytes32) { - return DOMAIN_SEPARATOR_SCHEMA_HASH; + return EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH; } function publicAddFillResults(FillResults memory totalFillResults, FillResults memory singleFillResults) diff --git a/packages/contracts/src/utils/order_factory.ts b/packages/contracts/src/utils/order_factory.ts index 6e4c9a883..009dbc396 100644 --- a/packages/contracts/src/utils/order_factory.ts +++ b/packages/contracts/src/utils/order_factory.ts @@ -26,7 +26,7 @@ export class OrderFactory { ...this._defaultOrderParams, ...customOrderParams, } as any) as Order; - const orderHashBuff = orderHashUtils.getOrderHashBuff(order); + const orderHashBuff = orderHashUtils.getOrderHashBuffer(order); const signature = signingUtils.signMessage(orderHashBuff, this._privateKey, signatureType); const signedOrder = { ...order, diff --git a/packages/contracts/src/utils/transaction_factory.ts b/packages/contracts/src/utils/transaction_factory.ts index a060263b1..19ef4e1bf 100644 --- a/packages/contracts/src/utils/transaction_factory.ts +++ b/packages/contracts/src/utils/transaction_factory.ts @@ -1,10 +1,19 @@ -import { crypto, generatePseudoRandomSalt } from '@0xproject/order-utils'; +import { EIP712Schema, EIP712Types, EIP712Utils, generatePseudoRandomSalt } from '@0xproject/order-utils'; import { SignatureType } from '@0xproject/types'; import * as ethUtil from 'ethereumjs-util'; import { signingUtils } from './signing_utils'; import { SignedTransaction } from './types'; +const EIP712_ZEROEX_TRANSACTION_SCHEMA: EIP712Schema = { + name: 'ZeroExTransaction', + parameters: [ + { name: 'salt', type: EIP712Types.Uint256 }, + { name: 'signer', type: EIP712Types.Address }, + { name: 'data', type: EIP712Types.Bytes }, + ], +}; + export class TransactionFactory { private _signerBuff: Buffer; private _exchangeAddress: string; @@ -16,14 +25,22 @@ export class TransactionFactory { } public newSignedTransaction(data: string, signatureType: SignatureType = SignatureType.EthSign): SignedTransaction { const salt = generatePseudoRandomSalt(); - const txHash = crypto.solSHA3([this._exchangeAddress, this._signerBuff, salt, ethUtil.toBuffer(data)]); + const signer = `0x${this._signerBuff.toString('hex')}`; + const executeTransactionData = { + salt, + signer, + data, + }; + const executeTransactionHashBuff = EIP712Utils.structHash( + EIP712_ZEROEX_TRANSACTION_SCHEMA, + executeTransactionData, + ); + const txHash = EIP712Utils.createEIP712Message(executeTransactionHashBuff, this._exchangeAddress); const signature = signingUtils.signMessage(txHash, this._privateKey, signatureType); const signedTx = { exchangeAddress: this._exchangeAddress, - salt, - signer: `0x${this._signerBuff.toString('hex')}`, - data, signature: `0x${signature.toString('hex')}`, + ...executeTransactionData, }; return signedTx; } diff --git a/packages/contracts/test/exchange/libs.ts b/packages/contracts/test/exchange/libs.ts index eff05981d..c08001198 100644 --- a/packages/contracts/test/exchange/libs.ts +++ b/packages/contracts/test/exchange/libs.ts @@ -1,5 +1,5 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; -import { assetProxyUtils, orderHashUtils } from '@0xproject/order-utils'; +import { assetProxyUtils, EIP712Utils, orderHashUtils } from '@0xproject/order-utils'; import { SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; @@ -56,13 +56,17 @@ describe('Exchange libs', () => { describe('getOrderSchema', () => { it('should output the correct order schema hash', async () => { const orderSchema = await libs.getOrderSchemaHash.callAsync(); - expect(orderHashUtils._getOrderSchemaHex()).to.be.equal(orderSchema); + const schemaHashBuffer = orderHashUtils._getOrderSchemaBuffer(); + const schemaHashHex = `0x${schemaHashBuffer.toString('hex')}`; + expect(schemaHashHex).to.be.equal(orderSchema); }); }); describe('getDomainSeparatorSchema', () => { it('should output the correct domain separator schema hash', async () => { const domainSeparatorSchema = await libs.getDomainSeparatorSchemaHash.callAsync(); - expect(orderHashUtils._getDomainSeparatorSchemaHex()).to.be.equal(domainSeparatorSchema); + const domainSchemaBuffer = EIP712Utils._getDomainSeparatorSchemaBuffer(); + const schemaHashHex = `0x${domainSchemaBuffer.toString('hex')}`; + expect(schemaHashHex).to.be.equal(domainSeparatorSchema); }); }); describe('getOrderHash', () => { diff --git a/packages/order-utils/src/eip712_utils.ts b/packages/order-utils/src/eip712_utils.ts new file mode 100644 index 000000000..2594e6d6d --- /dev/null +++ b/packages/order-utils/src/eip712_utils.ts @@ -0,0 +1,93 @@ +import ethUtil = require('ethereumjs-util'); +import * as _ from 'lodash'; + +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; + +const EIP712_DOMAIN_SCHEMA: EIP712Schema = { + name: 'EIP712Domain', + parameters: [ + { name: 'name', type: EIP712Types.String }, + { name: 'version', type: EIP712Types.String }, + { name: 'verifyingContract', type: EIP712Types.Address }, + ], +}; + +export const EIP712Utils = { + /** + * Compiles the EIP712Schema and returns the hash of the schema. + * @param schema The EIP712 schema. + * @return The hash of the compiled schema + */ + compileSchema(schema: EIP712Schema): Buffer { + const eip712Schema = EIP712Utils._encodeType(schema); + const eip712SchemaHashBuffer = crypto.solSHA3([eip712Schema]); + return eip712SchemaHashBuffer; + }, + /** + * Merges the EIP712 hash of a struct with the DomainSeparator for 0x v2. + * @param hashStruct the EIP712 hash of a struct + * @param contractAddress the exchange contract address + * @return The hash of an EIP712 message with domain separator prefixed + */ + createEIP712Message(hashStruct: Buffer, contractAddress: string): Buffer { + const domainSeparatorHashBuffer = EIP712Utils._getDomainSeparatorHashBuffer(contractAddress); + const messageBuff = crypto.solSHA3([EIP191_PREFIX, domainSeparatorHashBuffer, hashStruct]); + return messageBuff; + }, + pad32Address(address: string): Buffer { + const addressBuffer = ethUtil.toBuffer(address); + const addressPadded = EIP712Utils.pad32Buffer(addressBuffer); + return addressPadded; + }, + pad32Buffer(buffer: Buffer): Buffer { + const bufferPadded = ethUtil.setLengthLeft(buffer, EIP712_VALUE_LENGTH); + return bufferPadded; + }, + _getDomainSeparatorSchemaBuffer(): Buffer { + return EIP712Utils.compileSchema(EIP712_DOMAIN_SCHEMA); + }, + _getDomainSeparatorHashBuffer(exchangeAddress: string): Buffer { + const domainSeparatorSchemaBuffer = EIP712Utils._getDomainSeparatorSchemaBuffer(); + const encodedData = EIP712Utils._encodeData(EIP712_DOMAIN_SCHEMA, { + name: EIP712_DOMAIN_NAME, + version: EIP712_DOMAIN_VERSION, + verifyingContract: exchangeAddress, + }); + const domainSeparatorHashBuff2 = crypto.solSHA3([domainSeparatorSchemaBuffer, ...encodedData]); + return domainSeparatorHashBuff2; + }, + _encodeType(schema: EIP712Schema): string { + const namedTypes = _.map(schema.parameters, ({ name, type }) => `${type} ${name}`); + const namedTypesJoined = namedTypes.join(','); + const encodedType = `${schema.name}(${namedTypesJoined})`; + return encodedType; + }, + _encodeData(schema: EIP712Schema, data: { [key: string]: any }): any { + const encodedValues = []; + for (const parameter of schema.parameters) { + const value = data[parameter.name]; + if (parameter.type === EIP712Types.String || parameter.type === EIP712Types.Bytes) { + encodedValues.push(crypto.solSHA3([ethUtil.toBuffer(value)])); + } else if (parameter.type === EIP712Types.Uint256) { + encodedValues.push(value); + } else if (parameter.type === EIP712Types.Address) { + encodedValues.push(EIP712Utils.pad32Address(value)); + } else { + throw new Error(`Unable to encode ${parameter.type}`); + } + } + return encodedValues; + }, + structHash(schema: EIP712Schema, data: { [key: string]: any }): Buffer { + const encodedData = EIP712Utils._encodeData(schema, data); + const schemaHash = EIP712Utils.compileSchema(schema); + const hashBuffer = crypto.solSHA3([schemaHash, ...encodedData]); + return hashBuffer; + }, +}; diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts index f9b37df82..514488e02 100644 --- a/packages/order-utils/src/index.ts +++ b/packages/order-utils/src/index.ts @@ -13,11 +13,12 @@ export { orderFactory } from './order_factory'; export { constants } from './constants'; export { crypto } from './crypto'; export { generatePseudoRandomSalt } from './salt'; -export { OrderError, MessagePrefixType, MessagePrefixOpts } from './types'; +export { OrderError, MessagePrefixType, MessagePrefixOpts, 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 { RemainingFillableCalculator } from './remaining_fillable_calculator'; export { OrderStateUtils } from './order_state_utils'; export { assetProxyUtils } from './asset_proxy_utils'; +export { EIP712Utils } from './eip712_utils'; export { OrderValidationUtils } from './order_validation_utils'; export { ExchangeTransferSimulator } from './exchange_transfer_simulator'; diff --git a/packages/order-utils/src/order_hash.ts b/packages/order-utils/src/order_hash.ts index b9d0b43aa..54c500653 100644 --- a/packages/order-utils/src/order_hash.ts +++ b/packages/order-utils/src/order_hash.ts @@ -1,14 +1,31 @@ import { schemas, SchemaValidator } from '@0xproject/json-schemas'; import { Order, SignedOrder } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; -import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; import { assert } from './assert'; -import { crypto } from './crypto'; +import { EIP712Utils } from './eip712_utils'; +import { EIP712Schema, EIP712Types } from './types'; const INVALID_TAKER_FORMAT = 'instance.takerAddress is not of a type(s) string'; +const EIP712_ORDER_SCHEMA: EIP712Schema = { + name: 'Order', + parameters: [ + { name: 'makerAddress', type: EIP712Types.Address }, + { name: 'takerAddress', type: EIP712Types.Address }, + { name: 'feeRecipientAddress', type: EIP712Types.Address }, + { name: 'senderAddress', type: EIP712Types.Address }, + { name: 'makerAssetAmount', type: EIP712Types.Uint256 }, + { name: 'takerAssetAmount', type: EIP712Types.Uint256 }, + { name: 'makerFee', type: EIP712Types.Uint256 }, + { name: 'takerFee', type: EIP712Types.Uint256 }, + { name: 'expirationTimeSeconds', type: EIP712Types.Uint256 }, + { name: 'salt', type: EIP712Types.Uint256 }, + { name: 'makerAssetData', type: EIP712Types.Bytes }, + { name: 'takerAssetData', type: EIP712Types.Bytes }, + ], +}; + export const orderHashUtils = { /** * Checks if the supplied hex encoded order hash is valid. @@ -42,7 +59,7 @@ export const orderHashUtils = { throw error; } - const orderHashBuff = this.getOrderHashBuff(order); + const orderHashBuff = orderHashUtils.getOrderHashBuffer(order); const orderHashHex = `0x${orderHashBuff.toString('hex')}`; return orderHashHex; }, @@ -51,64 +68,12 @@ export const orderHashUtils = { * @param order An object that conforms to the Order or SignedOrder interface definitions. * @return The resulting orderHash from hashing the supplied order as a Buffer */ - getOrderHashBuff(order: SignedOrder | Order): Buffer { - const makerAssetDataHash = crypto.solSHA3([ethUtil.toBuffer(order.makerAssetData)]); - const takerAssetDataHash = crypto.solSHA3([ethUtil.toBuffer(order.takerAssetData)]); - - const orderParamsHashBuff = crypto.solSHA3([ - order.makerAddress, - order.takerAddress, - order.feeRecipientAddress, - order.senderAddress, - order.makerAssetAmount, - order.takerAssetAmount, - order.makerFee, - order.takerFee, - order.expirationTimeSeconds, - order.salt, - makerAssetDataHash, - takerAssetDataHash, - ]); - const orderParamsHashHex = `0x${orderParamsHashBuff.toString('hex')}`; - const orderSchemaHashHex = this._getOrderSchemaHex(); - const domainSeparatorHashHex = this._getDomainSeparatorHashHex(order.exchangeAddress); - const domainSeparatorSchemaHex = this._getDomainSeparatorSchemaHex(); - const orderHashBuff = crypto.solSHA3([ - new BigNumber(domainSeparatorSchemaHex), - new BigNumber(domainSeparatorHashHex), - new BigNumber(orderSchemaHashHex), - new BigNumber(orderParamsHashHex), - ]); + getOrderHashBuffer(order: SignedOrder | Order): Buffer { + const orderParamsHashBuff = EIP712Utils.structHash(EIP712_ORDER_SCHEMA, order); + const orderHashBuff = EIP712Utils.createEIP712Message(orderParamsHashBuff, order.exchangeAddress); return orderHashBuff; }, - _getOrderSchemaHex(): string { - const orderSchemaHashBuff = crypto.solSHA3([ - 'Order(', - 'address makerAddress,', - 'address takerAddress,', - 'address feeRecipientAddress,', - 'address senderAddress,', - 'uint256 makerAssetAmount,', - 'uint256 takerAssetAmount,', - 'uint256 makerFee,', - 'uint256 takerFee,', - 'uint256 expirationTimeSeconds,', - 'uint256 salt,', - 'bytes makerAssetData,', - 'bytes takerAssetData,', - ')', - ]); - const schemaHashHex = `0x${orderSchemaHashBuff.toString('hex')}`; - return schemaHashHex; - }, - _getDomainSeparatorSchemaHex(): string { - const domainSeparatorSchemaHashBuff = crypto.solSHA3(['DomainSeparator(address contract)']); - const schemaHashHex = `0x${domainSeparatorSchemaHashBuff.toString('hex')}`; - return schemaHashHex; - }, - _getDomainSeparatorHashHex(exchangeAddress: string): string { - const domainSeparatorHashBuff = crypto.solSHA3([exchangeAddress]); - const domainSeparatorHashHex = `0x${domainSeparatorHashBuff.toString('hex')}`; - return domainSeparatorHashHex; + _getOrderSchemaBuffer(): Buffer { + return EIP712Utils.compileSchema(EIP712_ORDER_SCHEMA); }, }; diff --git a/packages/order-utils/src/types.ts b/packages/order-utils/src/types.ts index 3f1fce66d..b08e74e71 100644 --- a/packages/order-utils/src/types.ts +++ b/packages/order-utils/src/types.ts @@ -33,3 +33,21 @@ export enum TransferType { Trade = 'trade', Fee = 'fee', } + +export interface EIP712Parameter { + name: string; + type: EIP712Types; +} + +export interface EIP712Schema { + name: string; + parameters: EIP712Parameter[]; +} + +export enum EIP712Types { + Address = 'address', + Bytes = 'bytes', + Bytes32 = 'bytes32', + String = 'string', + Uint256 = 'uint256', +} |