diff options
Diffstat (limited to 'packages')
50 files changed, 798 insertions, 516 deletions
diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json index 63e9997ff..1324a73d1 100644 --- a/packages/0x.js/package.json +++ b/packages/0x.js/package.json @@ -102,7 +102,7 @@ "@0xproject/assert": "^0.2.10", "@0xproject/base-contract": "^0.3.2", "@0xproject/contract-wrappers": "^0.0.2", - "@0xproject/order-utils": "^0.0.5", + "@0xproject/order-utils": "0.0.5", "@0xproject/order-watcher": "^0.0.2", "@0xproject/sol-compiler": "^0.5.0", "@0xproject/types": "0.7.0", diff --git a/packages/contract-wrappers/package.json b/packages/contract-wrappers/package.json index 8680613a3..61e92903f 100644 --- a/packages/contract-wrappers/package.json +++ b/packages/contract-wrappers/package.json @@ -80,7 +80,7 @@ "@0xproject/base-contract": "^0.3.2", "@0xproject/fill-scenarios": "^0.0.2", "@0xproject/json-schemas": "0.7.22", - "@0xproject/order-utils": "^0.0.5", + "@0xproject/order-utils": "0.0.5", "@0xproject/types": "0.7.0", "@0xproject/typescript-typings": "^0.3.2", "@0xproject/utils": "^0.6.2", diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 300a5331a..eee2b04d3 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -69,9 +69,9 @@ }, "dependencies": { "@0xproject/base-contract": "^0.3.2", - "@0xproject/order-utils": "^0.0.5", + "@0xproject/order-utils": "^0.0.6", "@0xproject/sol-compiler": "^0.5.0", - "@0xproject/types": "^0.7.0", + "@0xproject/types": "^1.0.0", "@0xproject/typescript-typings": "^0.3.2", "@0xproject/utils": "^0.6.2", "@0xproject/web3-wrapper": "^0.6.4", diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol index d40974e5f..79dc87af0 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol @@ -97,7 +97,7 @@ contract MixinSignatureValidator is bytes32 r; bytes32 s; address recovered; - + // Always illegal signature. // This is always an implicit option since a signer can create a // signature array with invalid type or length. We may as well make diff --git a/packages/contracts/src/utils/address_utils.ts b/packages/contracts/src/utils/address_utils.ts index dc63459f9..a9fb6921a 100644 --- a/packages/contracts/src/utils/address_utils.ts +++ b/packages/contracts/src/utils/address_utils.ts @@ -1,6 +1,4 @@ -import { generatePseudoRandomSalt } from '@0xproject/order-utils'; - -import { crypto } from './crypto'; +import { crypto, generatePseudoRandomSalt } from '@0xproject/order-utils'; export const addressUtils = { generatePseudoRandomAddress(): string { diff --git a/packages/contracts/src/utils/exchange_wrapper.ts b/packages/contracts/src/utils/exchange_wrapper.ts index 14d183b09..dd278e77c 100644 --- a/packages/contracts/src/utils/exchange_wrapper.ts +++ b/packages/contracts/src/utils/exchange_wrapper.ts @@ -1,4 +1,4 @@ -import { SignedOrder } from '@0xproject/types'; +import { AssetProxyId, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import { LogEntry, Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types'; @@ -10,7 +10,7 @@ import { constants } from './constants'; import { formatters } from './formatters'; import { LogDecoder } from './log_decoder'; import { orderUtils } from './order_utils'; -import { AssetProxyId, OrderInfo, SignedTransaction } from './types'; +import { OrderInfo, SignedTransaction } from './types'; export class ExchangeWrapper { private _exchange: ExchangeContract; diff --git a/packages/contracts/src/utils/formatters.ts b/packages/contracts/src/utils/formatters.ts index c46d668bc..1035f2d7c 100644 --- a/packages/contracts/src/utils/formatters.ts +++ b/packages/contracts/src/utils/formatters.ts @@ -13,8 +13,8 @@ export const formatters = { takerAssetFillAmounts, }; _.forEach(signedOrders, signedOrder => { - const orderStruct = orderUtils.getOrderStruct(signedOrder); - batchFill.orders.push(orderStruct); + const orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder); + batchFill.orders.push(orderWithoutExchangeAddress); batchFill.signatures.push(signedOrder.signature); if (takerAssetFillAmounts.length < signedOrders.length) { batchFill.takerAssetFillAmounts.push(signedOrder.takerAssetAmount); @@ -29,8 +29,8 @@ export const formatters = { takerAssetFillAmount, }; _.forEach(signedOrders, signedOrder => { - const orderStruct = orderUtils.getOrderStruct(signedOrder); - marketSellOrders.orders.push(orderStruct); + const orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder); + marketSellOrders.orders.push(orderWithoutExchangeAddress); marketSellOrders.signatures.push(signedOrder.signature); }); return marketSellOrders; @@ -42,8 +42,8 @@ export const formatters = { makerAssetFillAmount, }; _.forEach(signedOrders, signedOrder => { - const orderStruct = orderUtils.getOrderStruct(signedOrder); - marketBuyOrders.orders.push(orderStruct); + const orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder); + marketBuyOrders.orders.push(orderWithoutExchangeAddress); marketBuyOrders.signatures.push(signedOrder.signature); }); return marketBuyOrders; @@ -53,8 +53,8 @@ export const formatters = { orders: [], }; _.forEach(signedOrders, signedOrder => { - const orderStruct = orderUtils.getOrderStruct(signedOrder); - batchCancel.orders.push(orderStruct); + const orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder); + batchCancel.orders.push(orderWithoutExchangeAddress); }); return batchCancel; }, diff --git a/packages/contracts/src/utils/match_order_tester.ts b/packages/contracts/src/utils/match_order_tester.ts index ec8931a20..09c0d8083 100644 --- a/packages/contracts/src/utils/match_order_tester.ts +++ b/packages/contracts/src/utils/match_order_tester.ts @@ -1,5 +1,6 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; -import { SignedOrder } from '@0xproject/types'; +import { assetProxyUtils, crypto, orderHashUtils } from '@0xproject/order-utils'; +import { AssetProxyId, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; import { LogWithDecodedArgs } from 'ethereum-types'; @@ -15,17 +16,13 @@ import { ExchangeContract, FillContractEventArgs, } from '../contract_wrappers/generated/exchange'; -import { assetProxyUtils } from '../utils/asset_proxy_utils'; import { chaiSetup } from '../utils/chai_setup'; import { constants } from '../utils/constants'; -import { crypto } from '../utils/crypto'; import { ERC20Wrapper } from '../utils/erc20_wrapper'; import { ERC721Wrapper } from '../utils/erc721_wrapper'; import { ExchangeWrapper } from '../utils/exchange_wrapper'; import { OrderFactory } from '../utils/order_factory'; -import { orderUtils } from '../utils/order_utils'; import { - AssetProxyId, ContractName, ERC20BalancesByOwner, ERC721TokenIdsByOwner, @@ -123,7 +120,7 @@ export class MatchOrderTester { const feeRecipientAddressRight = signedOrderRight.feeRecipientAddress; // Verify Left order preconditions const orderTakerAssetFilledAmountLeft = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( - orderUtils.getOrderHashHex(signedOrderLeft), + orderHashUtils.getOrderHashHex(signedOrderLeft), ); const expectedOrderFilledAmountLeft = initialTakerAssetFilledAmountLeft ? initialTakerAssetFilledAmountLeft @@ -131,7 +128,7 @@ export class MatchOrderTester { expect(expectedOrderFilledAmountLeft).to.be.bignumber.equal(orderTakerAssetFilledAmountLeft); // Verify Right order preconditions const orderTakerAssetFilledAmountRight = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( - orderUtils.getOrderHashHex(signedOrderRight), + orderHashUtils.getOrderHashHex(signedOrderRight), ); const expectedOrderFilledAmountRight = initialTakerAssetFilledAmountRight ? initialTakerAssetFilledAmountRight @@ -182,7 +179,7 @@ export class MatchOrderTester { orderTakerAssetFilledAmountRight: BigNumber, ): Promise<TransferAmounts> { let amountBoughtByLeftMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( - orderUtils.getOrderHashHex(signedOrderLeft), + orderHashUtils.getOrderHashHex(signedOrderLeft), ); amountBoughtByLeftMaker = amountBoughtByLeftMaker.minus(orderTakerAssetFilledAmountLeft); const amountSoldByLeftMaker = amountBoughtByLeftMaker @@ -193,7 +190,7 @@ export class MatchOrderTester { .dividedToIntegerBy(signedOrderRight.makerAssetAmount); const amountReceivedByTaker = amountSoldByLeftMaker.minus(amountReceivedByRightMaker); let amountBoughtByRightMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( - orderUtils.getOrderHashHex(signedOrderRight), + orderHashUtils.getOrderHashHex(signedOrderRight), ); amountBoughtByRightMaker = amountBoughtByRightMaker.minus(orderTakerAssetFilledAmountRight); const amountSoldByRightMaker = amountBoughtByRightMaker diff --git a/packages/contracts/src/utils/order_factory.ts b/packages/contracts/src/utils/order_factory.ts index e60f25605..af411c01f 100644 --- a/packages/contracts/src/utils/order_factory.ts +++ b/packages/contracts/src/utils/order_factory.ts @@ -1,23 +1,21 @@ -import { generatePseudoRandomSalt } from '@0xproject/order-utils'; -import { SignedOrder, UnsignedOrder } from '@0xproject/types'; +import { generatePseudoRandomSalt, orderHashUtils } from '@0xproject/order-utils'; +import { Order, SignatureType, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; import { constants } from './constants'; -import { orderUtils } from './order_utils'; import { signingUtils } from './signing_utils'; -import { SignatureType } from './types'; export class OrderFactory { - private _defaultOrderParams: Partial<UnsignedOrder>; + private _defaultOrderParams: Partial<Order>; private _privateKey: Buffer; - constructor(privateKey: Buffer, defaultOrderParams: Partial<UnsignedOrder>) { + constructor(privateKey: Buffer, defaultOrderParams: Partial<Order>) { this._defaultOrderParams = defaultOrderParams; this._privateKey = privateKey; } public newSignedOrder( - customOrderParams: Partial<UnsignedOrder> = {}, - signatureType: SignatureType = SignatureType.Ecrecover, + customOrderParams: Partial<Order> = {}, + signatureType: SignatureType = SignatureType.EthSign, ): SignedOrder { const tenMinutes = 10 * 60 * 1000; const randomExpiration = new BigNumber(Date.now() + tenMinutes); @@ -28,8 +26,8 @@ export class OrderFactory { takerAddress: constants.NULL_ADDRESS, ...this._defaultOrderParams, ...customOrderParams, - } as any) as UnsignedOrder; - const orderHashBuff = orderUtils.getOrderHashBuff(order); + } as any) as Order; + const orderHashBuff = orderHashUtils.getOrderHashBuff(order); const signature = signingUtils.signMessage(orderHashBuff, this._privateKey, signatureType); const signedOrder = { ...order, diff --git a/packages/contracts/src/utils/order_utils.ts b/packages/contracts/src/utils/order_utils.ts index 6d1aaa06b..0d0329aa1 100644 --- a/packages/contracts/src/utils/order_utils.ts +++ b/packages/contracts/src/utils/order_utils.ts @@ -1,14 +1,13 @@ -import { Order, SignedOrder, UnsignedOrder } from '@0xproject/types'; +import { Order, OrderWithoutExchangeAddress, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import ethUtil = require('ethereumjs-util'); -import { crypto } from './crypto'; import { CancelOrder, MatchOrder } from './types'; export const orderUtils = { createFill: (signedOrder: SignedOrder, takerAssetFillAmount?: BigNumber) => { const fill = { - order: orderUtils.getOrderStruct(signedOrder), + order: orderUtils.getOrderWithoutExchangeAddress(signedOrder), takerAssetFillAmount: takerAssetFillAmount || signedOrder.takerAssetAmount, signature: signedOrder.signature, }; @@ -16,12 +15,12 @@ export const orderUtils = { }, createCancel(signedOrder: SignedOrder, takerAssetCancelAmount?: BigNumber): CancelOrder { const cancel = { - order: orderUtils.getOrderStruct(signedOrder), + order: orderUtils.getOrderWithoutExchangeAddress(signedOrder), takerAssetCancelAmount: takerAssetCancelAmount || signedOrder.takerAssetAmount, }; return cancel; }, - getOrderStruct(signedOrder: SignedOrder): Order { + getOrderWithoutExchangeAddress(signedOrder: SignedOrder): OrderWithoutExchangeAddress { const orderStruct = { senderAddress: signedOrder.senderAddress, makerAddress: signedOrder.makerAddress, @@ -38,75 +37,10 @@ export const orderUtils = { }; return orderStruct; }, - 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; - }, - 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; - }, - getOrderHashBuff(order: SignedOrder | UnsignedOrder): 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 = orderUtils.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), - ]); - return orderHashBuff; - }, - getOrderHashHex(order: SignedOrder | UnsignedOrder): string { - const orderHashBuff = orderUtils.getOrderHashBuff(order); - const orderHashHex = `0x${orderHashBuff.toString('hex')}`; - return orderHashHex; - }, createMatchOrders(signedOrderLeft: SignedOrder, signedOrderRight: SignedOrder): MatchOrder { const fill = { - left: orderUtils.getOrderStruct(signedOrderLeft), - right: orderUtils.getOrderStruct(signedOrderRight), + left: orderUtils.getOrderWithoutExchangeAddress(signedOrderLeft), + right: orderUtils.getOrderWithoutExchangeAddress(signedOrderRight), leftSignature: signedOrderLeft.signature, rightSignature: signedOrderRight.signature, }; diff --git a/packages/contracts/src/utils/signing_utils.ts b/packages/contracts/src/utils/signing_utils.ts index 4c36c8310..9c711c72c 100644 --- a/packages/contracts/src/utils/signing_utils.ts +++ b/packages/contracts/src/utils/signing_utils.ts @@ -1,10 +1,9 @@ +import { SignatureType } from '@0xproject/types'; import * as ethUtil from 'ethereumjs-util'; -import { SignatureType } from './types'; - export const signingUtils = { signMessage(message: Buffer, privateKey: Buffer, signatureType: SignatureType): Buffer { - if (signatureType === SignatureType.Ecrecover) { + if (signatureType === SignatureType.EthSign) { const prefixedMessage = ethUtil.hashPersonalMessage(message); const ecSignature = ethUtil.ecsign(prefixedMessage, privateKey); const signature = Buffer.concat([ diff --git a/packages/contracts/src/utils/transaction_factory.ts b/packages/contracts/src/utils/transaction_factory.ts index 65cdb3f89..434611908 100644 --- a/packages/contracts/src/utils/transaction_factory.ts +++ b/packages/contracts/src/utils/transaction_factory.ts @@ -1,10 +1,10 @@ -import { generatePseudoRandomSalt } from '@0xproject/order-utils'; +import { crypto, generatePseudoRandomSalt } from '@0xproject/order-utils'; +import { SignatureType } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as ethUtil from 'ethereumjs-util'; -import { crypto } from './crypto'; import { signingUtils } from './signing_utils'; -import { SignatureType, SignedTransaction } from './types'; +import { SignedTransaction } from './types'; export class TransactionFactory { private _signerBuff: Buffer; @@ -15,10 +15,7 @@ export class TransactionFactory { this._exchangeAddress = exchangeAddress; this._signerBuff = ethUtil.privateToAddress(this._privateKey); } - public newSignedTransaction( - data: string, - signatureType: SignatureType = SignatureType.Ecrecover, - ): SignedTransaction { + 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 signature = signingUtils.signMessage(txHash, this._privateKey, signatureType); diff --git a/packages/contracts/src/utils/types.ts b/packages/contracts/src/utils/types.ts index d68a81b51..6340c4a51 100644 --- a/packages/contracts/src/utils/types.ts +++ b/packages/contracts/src/utils/types.ts @@ -1,4 +1,4 @@ -import { Order } from '@0xproject/types'; +import { Order, OrderWithoutExchangeAddress } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { AbiDefinition, ContractAbi } from 'ethereum-types'; @@ -19,37 +19,31 @@ export interface SubmissionContractEventArgs { } export interface BatchFillOrders { - orders: Order[]; + orders: OrderWithoutExchangeAddress[]; signatures: string[]; takerAssetFillAmounts: BigNumber[]; } export interface MarketSellOrders { - orders: Order[]; + orders: OrderWithoutExchangeAddress[]; signatures: string[]; takerAssetFillAmount: BigNumber; } export interface MarketBuyOrders { - orders: Order[]; + orders: OrderWithoutExchangeAddress[]; signatures: string[]; makerAssetFillAmount: BigNumber; } export interface BatchCancelOrders { - orders: Order[]; + orders: OrderWithoutExchangeAddress[]; } export interface CancelOrdersBefore { salt: BigNumber; } -export enum AssetProxyId { - INVALID, - ERC20, - ERC721, -} - export interface TransactionDataParams { name: string; abi: AbiDefinition[]; @@ -115,18 +109,6 @@ export enum ContractName { Whitelist = 'Whitelist', } -export enum SignatureType { - Illegal, - Invalid, - EIP712, - Ecrecover, - TxOrigin, - Caller, - Contract, - PreSigned, - Trezor, -} - export interface SignedTransaction { exchangeAddress: string; salt: BigNumber; @@ -162,31 +144,14 @@ export interface OrderInfo { orderTakerAssetFilledAmount: BigNumber; } -export interface ERC20ProxyData { - assetProxyId: AssetProxyId; - tokenAddress: string; -} - -export interface ERC721ProxyData { - assetProxyId: AssetProxyId; - tokenAddress: string; - tokenId: BigNumber; -} - -export interface ProxyData { - assetProxyId: AssetProxyId; - tokenAddress?: string; - data?: any; -} - export interface CancelOrder { - order: Order; + order: OrderWithoutExchangeAddress; takerAssetCancelAmount: BigNumber; } export interface MatchOrder { - left: Order; - right: Order; + left: OrderWithoutExchangeAddress; + right: OrderWithoutExchangeAddress; leftSignature: string; rightSignature: string; } diff --git a/packages/contracts/test/asset_proxy/proxies.ts b/packages/contracts/test/asset_proxy/proxies.ts index 9bcdfa2b8..faab39759 100644 --- a/packages/contracts/test/asset_proxy/proxies.ts +++ b/packages/contracts/test/asset_proxy/proxies.ts @@ -1,4 +1,6 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; +import { assetProxyUtils } from '@0xproject/order-utils'; +import { AssetProxyId } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; import * as _ from 'lodash'; @@ -8,12 +10,10 @@ import { DummyERC20TokenContract } from '../../src/contract_wrappers/generated/d import { DummyERC721TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c721_token'; import { ERC20ProxyContract } from '../../src/contract_wrappers/generated/e_r_c20_proxy'; import { ERC721ProxyContract } from '../../src/contract_wrappers/generated/e_r_c721_proxy'; -import { assetProxyUtils } from '../../src/utils/asset_proxy_utils'; import { chaiSetup } from '../../src/utils/chai_setup'; import { constants } from '../../src/utils/constants'; import { ERC20Wrapper } from '../../src/utils/erc20_wrapper'; import { ERC721Wrapper } from '../../src/utils/erc721_wrapper'; -import { AssetProxyId } from '../../src/utils/types'; import { provider, web3Wrapper } from '../../src/utils/web3_wrapper'; chaiSetup.configure(); diff --git a/packages/contracts/test/exchange/core.ts b/packages/contracts/test/exchange/core.ts index 6282a51ea..8320e97d6 100644 --- a/packages/contracts/test/exchange/core.ts +++ b/packages/contracts/test/exchange/core.ts @@ -1,5 +1,6 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; -import { SignedOrder } from '@0xproject/types'; +import { assetProxyUtils, crypto, orderHashUtils } from '@0xproject/order-utils'; +import { AssetProxyId, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as chai from 'chai'; @@ -18,16 +19,13 @@ import { FillContractEventArgs, } from '../../src/contract_wrappers/generated/exchange'; import { artifacts } from '../../src/utils/artifacts'; -import { assetProxyUtils } from '../../src/utils/asset_proxy_utils'; import { chaiSetup } from '../../src/utils/chai_setup'; import { constants } from '../../src/utils/constants'; -import { crypto } from '../../src/utils/crypto'; import { ERC20Wrapper } from '../../src/utils/erc20_wrapper'; import { ERC721Wrapper } from '../../src/utils/erc721_wrapper'; import { ExchangeWrapper } from '../../src/utils/exchange_wrapper'; import { OrderFactory } from '../../src/utils/order_factory'; -import { orderUtils } from '../../src/utils/order_utils'; -import { AssetProxyId, ContractName, ERC20BalancesByOwner, ExchangeStatus } from '../../src/utils/types'; +import { ContractName, ERC20BalancesByOwner, ExchangeStatus } from '../../src/utils/types'; import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper'; chaiSetup.configure(); @@ -128,7 +126,6 @@ describe('Exchange core', () => { afterEach(async () => { await blockchainLifecycle.revertAsync(); }); - describe('fillOrder', () => { beforeEach(async () => { erc20Balances = await erc20Wrapper.getBalancesAsync(); @@ -142,7 +139,7 @@ describe('Exchange core', () => { }); const takerAssetFilledAmountBefore = await exchangeWrapper.getTakerAssetFilledAmountAsync( - orderUtils.getOrderHashHex(signedOrder), + orderHashUtils.getOrderHashHex(signedOrder), ); expect(takerAssetFilledAmountBefore).to.be.bignumber.equal(0); @@ -152,7 +149,7 @@ describe('Exchange core', () => { }); const takerAssetFilledAmountAfter1 = await exchangeWrapper.getTakerAssetFilledAmountAsync( - orderUtils.getOrderHashHex(signedOrder), + orderHashUtils.getOrderHashHex(signedOrder), ); expect(takerAssetFilledAmountAfter1).to.be.bignumber.equal(fillTakerAssetAmount1); @@ -162,7 +159,7 @@ describe('Exchange core', () => { }); const takerAssetFilledAmountAfter2 = await exchangeWrapper.getTakerAssetFilledAmountAsync( - orderUtils.getOrderHashHex(signedOrder), + orderHashUtils.getOrderHashHex(signedOrder), ); expect(takerAssetFilledAmountAfter2).to.be.bignumber.equal(takerAssetFilledAmountAfter1); }); @@ -174,7 +171,7 @@ describe('Exchange core', () => { }); const takerAssetFilledAmountBefore = await exchangeWrapper.getTakerAssetFilledAmountAsync( - orderUtils.getOrderHashHex(signedOrder), + orderHashUtils.getOrderHashHex(signedOrder), ); expect(takerAssetFilledAmountBefore).to.be.bignumber.equal(0); @@ -182,7 +179,7 @@ describe('Exchange core', () => { await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }); const makerAmountBoughtAfter = await exchangeWrapper.getTakerAssetFilledAmountAsync( - orderUtils.getOrderHashHex(signedOrder), + orderHashUtils.getOrderHashHex(signedOrder), ); expect(makerAmountBoughtAfter).to.be.bignumber.equal(takerAssetFillAmount); @@ -227,7 +224,7 @@ describe('Exchange core', () => { }); const takerAssetFilledAmountBefore = await exchangeWrapper.getTakerAssetFilledAmountAsync( - orderUtils.getOrderHashHex(signedOrder), + orderHashUtils.getOrderHashHex(signedOrder), ); expect(takerAssetFilledAmountBefore).to.be.bignumber.equal(0); @@ -235,7 +232,7 @@ describe('Exchange core', () => { await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }); const makerAmountBoughtAfter = await exchangeWrapper.getTakerAssetFilledAmountAsync( - orderUtils.getOrderHashHex(signedOrder), + orderHashUtils.getOrderHashHex(signedOrder), ); expect(makerAmountBoughtAfter).to.be.bignumber.equal(takerAssetFillAmount); @@ -280,7 +277,7 @@ describe('Exchange core', () => { }); const takerAssetFilledAmountBefore = await exchangeWrapper.getTakerAssetFilledAmountAsync( - orderUtils.getOrderHashHex(signedOrder), + orderHashUtils.getOrderHashHex(signedOrder), ); expect(takerAssetFilledAmountBefore).to.be.bignumber.equal(0); @@ -288,7 +285,7 @@ describe('Exchange core', () => { await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }); const makerAmountBoughtAfter = await exchangeWrapper.getTakerAssetFilledAmountAsync( - orderUtils.getOrderHashHex(signedOrder), + orderHashUtils.getOrderHashHex(signedOrder), ); expect(makerAmountBoughtAfter).to.be.bignumber.equal(takerAssetFillAmount); @@ -334,7 +331,7 @@ describe('Exchange core', () => { }); const takerAssetFilledAmountBefore = await exchangeWrapper.getTakerAssetFilledAmountAsync( - orderUtils.getOrderHashHex(signedOrder), + orderHashUtils.getOrderHashHex(signedOrder), ); expect(takerAssetFilledAmountBefore).to.be.bignumber.equal(0); @@ -342,7 +339,7 @@ describe('Exchange core', () => { await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }); const makerAmountBoughtAfter = await exchangeWrapper.getTakerAssetFilledAmountAsync( - orderUtils.getOrderHashHex(signedOrder), + orderHashUtils.getOrderHashHex(signedOrder), ); const expectedMakerAmountBoughtAfter = takerAssetFillAmount.add(takerAssetFilledAmountBefore); expect(makerAmountBoughtAfter).to.be.bignumber.equal(expectedMakerAmountBoughtAfter); @@ -442,7 +439,7 @@ describe('Exchange core', () => { expect(expectedFilledTakerAssetAmount).to.be.bignumber.equal(logArgs.takerAssetFilledAmount); expect(expectedFeeMPaid).to.be.bignumber.equal(logArgs.makerFeePaid); expect(expectedFeeTPaid).to.be.bignumber.equal(logArgs.takerFeePaid); - expect(orderUtils.getOrderHashHex(signedOrder)).to.be.equal(logArgs.orderHash); + expect(orderHashUtils.getOrderHashHex(signedOrder)).to.be.equal(logArgs.orderHash); }); it('should throw when taker is specified and order is claimed by other', async () => { @@ -536,12 +533,6 @@ describe('Exchange core', () => { await expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith( constants.REVERT, ); - await web3Wrapper.awaitTransactionSuccessAsync( - await erc20TokenA.approve.sendTransactionAsync(erc20Proxy.address, constants.INITIAL_ERC20_ALLOWANCE, { - from: makerAddress, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); }); it('should throw if taker allowances are too low to fill order', async () => { @@ -557,12 +548,6 @@ describe('Exchange core', () => { await expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith( constants.REVERT, ); - await web3Wrapper.awaitTransactionSuccessAsync( - await erc20TokenB.approve.sendTransactionAsync(erc20Proxy.address, constants.INITIAL_ERC20_ALLOWANCE, { - from: takerAddress, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); }); it('should not change erc20Balances if an order is expired', async () => { @@ -653,7 +638,7 @@ describe('Exchange core', () => { expect(signedOrder.feeRecipientAddress).to.be.equal(logArgs.feeRecipientAddress); expect(signedOrder.makerAssetData).to.be.equal(logArgs.makerAssetData); expect(signedOrder.takerAssetData).to.be.equal(logArgs.takerAssetData); - expect(orderUtils.getOrderHashHex(signedOrder)).to.be.equal(logArgs.orderHash); + expect(orderHashUtils.getOrderHashHex(signedOrder)).to.be.equal(logArgs.orderHash); }); it('should log an error if already cancelled', async () => { diff --git a/packages/contracts/test/exchange/dispatcher.ts b/packages/contracts/test/exchange/dispatcher.ts index b9c7039bd..8bc66e3cf 100644 --- a/packages/contracts/test/exchange/dispatcher.ts +++ b/packages/contracts/test/exchange/dispatcher.ts @@ -1,4 +1,6 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; +import { assetProxyUtils } from '@0xproject/order-utils'; +import { AssetProxyId } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; import * as Web3 from 'web3'; @@ -8,12 +10,10 @@ import { ERC20ProxyContract } from '../../src/contract_wrappers/generated/e_r_c2 import { ERC721ProxyContract } from '../../src/contract_wrappers/generated/e_r_c721_proxy'; import { TestAssetProxyDispatcherContract } from '../../src/contract_wrappers/generated/test_asset_proxy_dispatcher'; import { artifacts } from '../../src/utils/artifacts'; -import { assetProxyUtils } from '../../src/utils/asset_proxy_utils'; import { chaiSetup } from '../../src/utils/chai_setup'; import { constants } from '../../src/utils/constants'; import { ERC20Wrapper } from '../../src/utils/erc20_wrapper'; import { ERC721Wrapper } from '../../src/utils/erc721_wrapper'; -import { AssetProxyId } from '../../src/utils/types'; import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper'; chaiSetup.configure(); diff --git a/packages/contracts/test/exchange/libs.ts b/packages/contracts/test/exchange/libs.ts index a3282876b..10cb8b34e 100644 --- a/packages/contracts/test/exchange/libs.ts +++ b/packages/contracts/test/exchange/libs.ts @@ -1,4 +1,5 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; +import { assetProxyUtils, orderHashUtils } from '@0xproject/order-utils'; import { SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; @@ -7,11 +8,9 @@ import ethUtil = require('ethereumjs-util'); import { TestLibsContract } from '../../src/contract_wrappers/generated/test_libs'; import { addressUtils } from '../../src/utils/address_utils'; import { artifacts } from '../../src/utils/artifacts'; -import { assetProxyUtils } from '../../src/utils/asset_proxy_utils'; import { chaiSetup } from '../../src/utils/chai_setup'; import { constants } from '../../src/utils/constants'; import { OrderFactory } from '../../src/utils/order_factory'; -import { orderUtils } from '../../src/utils/order_utils'; import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper'; chaiSetup.configure(); @@ -58,20 +57,20 @@ describe('Exchange libs', () => { describe('getOrderSchema', () => { it('should output the correct order schema hash', async () => { const orderSchema = await libs.getOrderSchemaHash.callAsync(); - expect(orderUtils.getOrderSchemaHex()).to.be.equal(orderSchema); + expect(orderHashUtils._getOrderSchemaHex()).to.be.equal(orderSchema); }); }); describe('getDomainSeparatorSchema', () => { it('should output the correct domain separator schema hash', async () => { const domainSeparatorSchema = await libs.getDomainSeparatorSchemaHash.callAsync(); - expect(orderUtils.getDomainSeparatorSchemaHex()).to.be.equal(domainSeparatorSchema); + expect(orderHashUtils._getDomainSeparatorSchemaHex()).to.be.equal(domainSeparatorSchema); }); }); describe('getOrderHash', () => { it('should output the correct orderHash', async () => { signedOrder = orderFactory.newSignedOrder(); const orderHashHex = await libs.publicGetOrderHash.callAsync(signedOrder); - expect(orderUtils.getOrderHashHex(signedOrder)).to.be.equal(orderHashHex); + expect(orderHashUtils.getOrderHashHex(signedOrder)).to.be.equal(orderHashHex); }); }); }); diff --git a/packages/contracts/test/exchange/match_orders.ts b/packages/contracts/test/exchange/match_orders.ts index c901c29b9..07295d78a 100644 --- a/packages/contracts/test/exchange/match_orders.ts +++ b/packages/contracts/test/exchange/match_orders.ts @@ -1,5 +1,6 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; -import { SignedOrder } from '@0xproject/types'; +import { assetProxyUtils, crypto } from '@0xproject/order-utils'; +import { AssetProxyId, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as chai from 'chai'; @@ -18,17 +19,14 @@ import { FillContractEventArgs, } from '../../src/contract_wrappers/generated/exchange'; import { artifacts } from '../../src/utils/artifacts'; -import { assetProxyUtils } from '../../src/utils/asset_proxy_utils'; import { chaiSetup } from '../../src/utils/chai_setup'; import { constants } from '../../src/utils/constants'; -import { crypto } from '../../src/utils/crypto'; import { ERC20Wrapper } from '../../src/utils/erc20_wrapper'; import { ERC721Wrapper } from '../../src/utils/erc721_wrapper'; import { ExchangeWrapper } from '../../src/utils/exchange_wrapper'; +import { MatchOrderTester } from '../../src/utils/match_order_tester'; import { OrderFactory } from '../../src/utils/order_factory'; -import { orderUtils } from '../../src/utils/order_utils'; import { - AssetProxyId, ContractName, ERC20BalancesByOwner, ERC721TokenIdsByOwner, @@ -37,8 +35,6 @@ import { } from '../../src/utils/types'; import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper'; -import { MatchOrderTester } from '../../src/utils/match_order_tester'; - chaiSetup.configure(); const expect = chai.expect; const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); diff --git a/packages/contracts/test/exchange/signature_validator.ts b/packages/contracts/test/exchange/signature_validator.ts index 1f030b742..936910ee9 100644 --- a/packages/contracts/test/exchange/signature_validator.ts +++ b/packages/contracts/test/exchange/signature_validator.ts @@ -1,4 +1,5 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; +import { assetProxyUtils, orderHashUtils } from '@0xproject/order-utils'; import { SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; @@ -7,11 +8,9 @@ import ethUtil = require('ethereumjs-util'); import { TestSignatureValidatorContract } from '../../src/contract_wrappers/generated/test_signature_validator'; import { addressUtils } from '../../src/utils/address_utils'; import { artifacts } from '../../src/utils/artifacts'; -import { assetProxyUtils } from '../../src/utils/asset_proxy_utils'; import { chaiSetup } from '../../src/utils/chai_setup'; import { constants } from '../../src/utils/constants'; import { OrderFactory } from '../../src/utils/order_factory'; -import { orderUtils } from '../../src/utils/order_utils'; import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper'; chaiSetup.configure(); @@ -64,7 +63,7 @@ describe('MixinSignatureValidator', () => { }); it('should return true with a valid signature', async () => { - const orderHashHex = orderUtils.getOrderHashHex(signedOrder); + const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync( orderHashHex, signedOrder.makerAddress, @@ -81,7 +80,7 @@ describe('MixinSignatureValidator', () => { const invalidSigBuff = Buffer.concat([v, invalidR, invalidS, signatureType]); const invalidSigHex = `0x${invalidSigBuff.toString('hex')}`; signedOrder.signature = invalidSigHex; - const orderHashHex = orderUtils.getOrderHashHex(signedOrder); + const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync( orderHashHex, signedOrder.makerAddress, diff --git a/packages/contracts/test/exchange/transactions.ts b/packages/contracts/test/exchange/transactions.ts index fc7aa8404..9af8b522b 100644 --- a/packages/contracts/test/exchange/transactions.ts +++ b/packages/contracts/test/exchange/transactions.ts @@ -1,6 +1,6 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; -import { generatePseudoRandomSalt } from '@0xproject/order-utils'; -import { Order, SignedOrder } from '@0xproject/types'; +import { assetProxyUtils, generatePseudoRandomSalt } from '@0xproject/order-utils'; +import { AssetProxyId, Order, OrderWithoutExchangeAddress, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; import * as ethUtil from 'ethereumjs-util'; @@ -11,7 +11,6 @@ import { ERC20ProxyContract } from '../../src/contract_wrappers/generated/e_r_c2 import { ExchangeContract } from '../../src/contract_wrappers/generated/exchange'; import { WhitelistContract } from '../../src/contract_wrappers/generated/whitelist'; import { artifacts } from '../../src/utils/artifacts'; -import { assetProxyUtils } from '../../src/utils/asset_proxy_utils'; import { chaiSetup } from '../../src/utils/chai_setup'; import { constants } from '../../src/utils/constants'; import { ERC20Wrapper } from '../../src/utils/erc20_wrapper'; @@ -19,13 +18,7 @@ import { ExchangeWrapper } from '../../src/utils/exchange_wrapper'; import { OrderFactory } from '../../src/utils/order_factory'; import { orderUtils } from '../../src/utils/order_utils'; import { TransactionFactory } from '../../src/utils/transaction_factory'; -import { - AssetProxyId, - ERC20BalancesByOwner, - ExchangeStatus, - SignatureType, - SignedTransaction, -} from '../../src/utils/types'; +import { ERC20BalancesByOwner, ExchangeStatus, SignedTransaction } from '../../src/utils/types'; import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper'; chaiSetup.configure(); @@ -48,7 +41,7 @@ describe('Exchange transactions', () => { let erc20Balances: ERC20BalancesByOwner; let signedOrder: SignedOrder; let signedTx: SignedTransaction; - let order: Order; + let orderWithoutExchangeAddress: OrderWithoutExchangeAddress; let orderFactory: OrderFactory; let makerTransactionFactory: TransactionFactory; let takerTransactionFactory: TransactionFactory; @@ -121,11 +114,11 @@ describe('Exchange transactions', () => { beforeEach(async () => { erc20Balances = await erc20Wrapper.getBalancesAsync(); signedOrder = orderFactory.newSignedOrder(); - order = orderUtils.getOrderStruct(signedOrder); + orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder); takerAssetFillAmount = signedOrder.takerAssetAmount.div(2); const data = exchange.fillOrder.getABIEncodedTransactionData( - order, + orderWithoutExchangeAddress, takerAssetFillAmount, signedOrder.signature, ); @@ -189,7 +182,7 @@ describe('Exchange transactions', () => { describe('cancelOrder', () => { beforeEach(async () => { - const data = exchange.cancelOrder.getABIEncodedTransactionData(order); + const data = exchange.cancelOrder.getABIEncodedTransactionData(orderWithoutExchangeAddress); signedTx = makerTransactionFactory.newSignedTransaction(data); }); @@ -248,12 +241,12 @@ describe('Exchange transactions', () => { await whitelist.updateWhitelistStatus.sendTransactionAsync(takerAddress, isApproved, { from: owner }), ); - const orderStruct = orderUtils.getOrderStruct(signedOrder); + orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder); const takerAssetFillAmount = signedOrder.takerAssetAmount; const salt = generatePseudoRandomSalt(); return expect( whitelist.fillOrderIfWhitelisted.sendTransactionAsync( - orderStruct, + orderWithoutExchangeAddress, takerAssetFillAmount, salt, signedOrder.signature, @@ -268,12 +261,12 @@ describe('Exchange transactions', () => { await whitelist.updateWhitelistStatus.sendTransactionAsync(makerAddress, isApproved, { from: owner }), ); - const orderStruct = orderUtils.getOrderStruct(signedOrder); + orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder); const takerAssetFillAmount = signedOrder.takerAssetAmount; const salt = generatePseudoRandomSalt(); return expect( whitelist.fillOrderIfWhitelisted.sendTransactionAsync( - orderStruct, + orderWithoutExchangeAddress, takerAssetFillAmount, salt, signedOrder.signature, @@ -292,12 +285,12 @@ describe('Exchange transactions', () => { await whitelist.updateWhitelistStatus.sendTransactionAsync(takerAddress, isApproved, { from: owner }), ); - const orderStruct = orderUtils.getOrderStruct(signedOrder); + orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder); const takerAssetFillAmount = signedOrder.takerAssetAmount; const salt = generatePseudoRandomSalt(); await web3Wrapper.awaitTransactionSuccessAsync( await whitelist.fillOrderIfWhitelisted.sendTransactionAsync( - orderStruct, + orderWithoutExchangeAddress, takerAssetFillAmount, salt, signedOrder.signature, diff --git a/packages/contracts/test/exchange/wrapper.ts b/packages/contracts/test/exchange/wrapper.ts index 7e1818f4a..a158ba8f3 100644 --- a/packages/contracts/test/exchange/wrapper.ts +++ b/packages/contracts/test/exchange/wrapper.ts @@ -1,5 +1,6 @@ import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils'; -import { SignedOrder } from '@0xproject/types'; +import { assetProxyUtils } from '@0xproject/order-utils'; +import { AssetProxyId, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as chai from 'chai'; @@ -14,14 +15,13 @@ import { ERC721ProxyContract } from '../../src/contract_wrappers/generated/e_r_c import { ExchangeContract } from '../../src/contract_wrappers/generated/exchange'; import { TokenRegistryContract } from '../../src/contract_wrappers/generated/token_registry'; import { artifacts } from '../../src/utils/artifacts'; -import { assetProxyUtils } from '../../src/utils/asset_proxy_utils'; import { chaiSetup } from '../../src/utils/chai_setup'; import { constants } from '../../src/utils/constants'; import { ERC20Wrapper } from '../../src/utils/erc20_wrapper'; import { ERC721Wrapper } from '../../src/utils/erc721_wrapper'; import { ExchangeWrapper } from '../../src/utils/exchange_wrapper'; import { OrderFactory } from '../../src/utils/order_factory'; -import { AssetProxyId, ERC20BalancesByOwner } from '../../src/utils/types'; +import { ERC20BalancesByOwner } from '../../src/utils/types'; import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper'; chaiSetup.configure(); diff --git a/packages/contracts/test/libraries/lib_bytes.ts b/packages/contracts/test/libraries/lib_bytes.ts index f8d75080a..26cfa8291 100644 --- a/packages/contracts/test/libraries/lib_bytes.ts +++ b/packages/contracts/test/libraries/lib_bytes.ts @@ -1,4 +1,5 @@ import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils'; +import { AssetProxyId } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import BN = require('bn.js'); @@ -11,7 +12,6 @@ import { TestLibBytesContract } from '../../src/contract_wrappers/generated/test import { artifacts } from '../../src/utils/artifacts'; import { chaiSetup } from '../../src/utils/chai_setup'; import { constants } from '../../src/utils/constants'; -import { AssetProxyId } from '../../src/utils/types'; import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper'; chaiSetup.configure(); diff --git a/packages/fill-scenarios/package.json b/packages/fill-scenarios/package.json index 6d47fe534..d0d3d9ec4 100644 --- a/packages/fill-scenarios/package.json +++ b/packages/fill-scenarios/package.json @@ -37,7 +37,7 @@ }, "dependencies": { "@0xproject/base-contract": "^0.3.2", - "@0xproject/order-utils": "^0.0.5", + "@0xproject/order-utils": "0.0.5", "@0xproject/types": "0.7.0", "ethereum-types": "^0.0.1", "@0xproject/typescript-typings": "^0.3.2", diff --git a/packages/json-schemas/package.json b/packages/json-schemas/package.json index 60a79d165..603e5ab73 100644 --- a/packages/json-schemas/package.json +++ b/packages/json-schemas/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/json-schemas", - "version": "0.7.24", + "version": "1.0.0", "engines": { "node": ">=6.12" }, diff --git a/packages/json-schemas/schemas/ec_signature_schema.ts b/packages/json-schemas/schemas/ec_signature_schema.ts new file mode 100644 index 000000000..71b840dd8 --- /dev/null +++ b/packages/json-schemas/schemas/ec_signature_schema.ts @@ -0,0 +1,20 @@ +export const ecSignatureParameterSchema = { + id: '/ECSignatureParameter', + type: 'string', + pattern: '^0[xX][0-9A-Fa-f]{64}$', +}; + +export const ecSignatureSchema = { + id: '/ECSignature', + properties: { + v: { + type: 'number', + minimum: 27, + maximum: 28, + }, + r: { $ref: '/ECSignatureParameter' }, + s: { $ref: '/ECSignatureParameter' }, + }, + required: ['v', 'r', 's'], + type: 'object', +}; diff --git a/packages/json-schemas/schemas/order_schemas.ts b/packages/json-schemas/schemas/order_schemas.ts index 183118c23..dcbfde6e0 100644 --- a/packages/json-schemas/schemas/order_schemas.ts +++ b/packages/json-schemas/schemas/order_schemas.ts @@ -5,11 +5,13 @@ export const orderSchema = { takerAddress: { $ref: '/Address' }, makerFee: { $ref: '/Number' }, takerFee: { $ref: '/Number' }, + senderAddress: { $ref: '/Address' }, makerAssetAmount: { $ref: '/Number' }, takerAssetAmount: { $ref: '/Number' }, makerAssetData: { $ref: '/Hex' }, takerAssetData: { $ref: '/Hex' }, salt: { $ref: '/Number' }, + exchangeAddress: { $ref: '/Address' }, feeRecipientAddress: { $ref: '/Address' }, expirationTimeSeconds: { $ref: '/Number' }, }, @@ -18,11 +20,13 @@ export const orderSchema = { 'takerAddress', 'makerFee', 'takerFee', + 'senderAddress', 'makerAssetAmount', 'takerAssetAmount', 'makerAssetData', 'takerAssetData', 'salt', + 'exchangeAddress', 'feeRecipientAddress', 'expirationTimeSeconds', ], diff --git a/packages/json-schemas/src/schemas.ts b/packages/json-schemas/src/schemas.ts index 26d6e6cf1..77ea88f5c 100644 --- a/packages/json-schemas/src/schemas.ts +++ b/packages/json-schemas/src/schemas.ts @@ -1,5 +1,6 @@ import { addressSchema, hexSchema, numberSchema } from '../schemas/basic_type_schemas'; import { blockParamSchema, blockRangeSchema } from '../schemas/block_range_schema'; +import { ecSignatureSchema } from '../schemas/ec_signature_schema'; 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'; @@ -31,6 +32,7 @@ export const schemas = { numberSchema, addressSchema, hexSchema, + ecSignatureSchema, indexFilterValuesSchema, orderCancellationRequestsSchema, orderFillOrKillRequestsSchema, diff --git a/packages/json-schemas/test/schema_test.ts b/packages/json-schemas/test/schema_test.ts index 379f92442..3858c7fa7 100644 --- a/packages/json-schemas/test/schema_test.ts +++ b/packages/json-schemas/test/schema_test.ts @@ -171,6 +171,7 @@ describe('Schema', () => { const order = { makerAddress: NULL_ADDRESS, takerAddress: NULL_ADDRESS, + senderAddress: NULL_ADDRESS, makerFee: '1', takerFee: '2', makerAssetAmount: '1', @@ -179,6 +180,7 @@ describe('Schema', () => { takerAssetData: NULL_ADDRESS, salt: '67006738228878699843088602623665307406148487219438534730168799356281242528500', feeRecipientAddress: NULL_ADDRESS, + exchangeAddress: NULL_ADDRESS, expirationTimeSeconds: '42', }; describe('#orderSchema', () => { diff --git a/packages/migrations/compiler.json b/packages/migrations/compiler.json index e1204b2d1..01fe0f194 100644 --- a/packages/migrations/compiler.json +++ b/packages/migrations/compiler.json @@ -1,17 +1,5 @@ { - "artifactsDir": "artifacts/1.0.0", "contractsDir": "../contracts/src/contracts", - "contracts": [ - "Exchange_v1", - "DummyERC20Token", - "ZRXToken", - "WETH9", - "TokenTransferProxy_v1", - "MultiSigWallet", - "MultiSigWalletWithTimeLock", - "MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress", - "TokenRegistry" - ], "compilerSettings": { "outputSelection": { "*": { diff --git a/packages/order-utils/package.json b/packages/order-utils/package.json index ddb95ba3f..2d7e7ba66 100644 --- a/packages/order-utils/package.json +++ b/packages/order-utils/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/order-utils", - "version": "0.0.5", + "version": "0.0.6", "engines": { "node": ">=6.12" }, @@ -9,13 +9,17 @@ "types": "lib/src/index.d.ts", "scripts": { "watch": "tsc -w", - "build": "tsc && copyfiles -u 3 './lib/src/monorepo_scripts/**/*' ./scripts", - "test": "run-s clean build run_mocha", + "build": "run-s clean update_artifacts generate_contract_wrappers transpile copy_monorepo_scripts", + "transpile": "tsc", + "copy_monorepo_scripts": "copyfiles -u 3 './lib/src/monorepo_scripts/**/*' ./scripts", + "generate_contract_wrappers": "abi-gen --abis 'lib/src/artifacts/@(Exchange|IWallet|IValidator).json' --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output src/generated_contract_wrappers --backend ethers && prettier --write 'src/generated_contract_wrappers/**.ts'", + "update_artifacts": "for i in ${npm_package_config_contracts}; do copyfiles -u 4 ../migrations/artifacts/2.0.0/$i.json lib/src/artifacts; done;", + "test": "run-s build run_mocha", "test:circleci": "yarn test:coverage", "run_mocha": "mocha lib/test/**/*_test.js --bail --exit", "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", "coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info", - "clean": "shx rm -rf lib scripts", + "clean": "shx rm -rf lib scripts lib/src/artifacts src/generated_contract_wrappers", "lint": "tslint --project .", "manual:postpublish": "yarn build; node ./scripts/postpublish.js", "docs:stage": "node scripts/stage_docs.js", @@ -23,6 +27,7 @@ "upload_docs_json": "aws s3 cp generated_docs/index.json $S3_URL --profile 0xproject --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --content-type application/json" }, "config": { + "contracts": "IWallet IValidator Exchange", "postpublish": { "docPublishConfigs": { "extraFileIncludes": [ @@ -66,15 +71,19 @@ }, "dependencies": { "@0xproject/assert": "^0.2.10", - "@0xproject/json-schemas": "0.7.22", - "@0xproject/types": "0.7.0", + "@0xproject/base-contract": "^0.3.2", + "@0xproject/json-schemas": "1.0.0", + "@0xproject/sol-compiler": "^0.5.0", + "@0xproject/types": "^1.0.0", "@0xproject/typescript-typings": "^0.3.2", "@0xproject/utils": "^0.6.2", "@0xproject/web3-wrapper": "^0.6.4", "@types/node": "^8.0.53", "bn.js": "^4.11.8", + "ethereum-types": "^0.0.1", "ethereumjs-abi": "^0.6.4", "ethereumjs-util": "^5.1.1", + "ethers": "^3.0.15", "lodash": "^4.17.4" }, "publishConfig": { diff --git a/packages/order-utils/src/artifacts.ts b/packages/order-utils/src/artifacts.ts new file mode 100644 index 000000000..f6fd00472 --- /dev/null +++ b/packages/order-utils/src/artifacts.ts @@ -0,0 +1,10 @@ +import { Artifact } from '@0xproject/types'; + +import * as Exchange from './artifacts/Exchange.json'; +import * as IValidator from './artifacts/IValidator.json'; +import * as IWallet from './artifacts/IWallet.json'; +export const artifacts = { + Exchange: (Exchange as any) as Artifact, + IWallet: (IWallet as any) as Artifact, + IValidator: (IValidator as any) as Artifact, +}; diff --git a/packages/order-utils/src/assert.ts b/packages/order-utils/src/assert.ts index 5ac402e7e..a1318b9b8 100644 --- a/packages/order-utils/src/assert.ts +++ b/packages/order-utils/src/assert.ts @@ -3,12 +3,12 @@ import { assert as sharedAssert } from '@0xproject/assert'; // tslint:disable-next-line:no-unused-variable import { Schema } from '@0xproject/json-schemas'; // tslint:disable-next-line:no-unused-variable -import { ECSignature } from '@0xproject/types'; +import { ECSignature, SignatureType } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as _ from 'lodash'; -import { isValidSignature } from './signature_utils'; +import { utils } from './utils'; export const assert = { ...sharedAssert, @@ -24,4 +24,14 @@ export const assert = { `Specified ${variableName} ${senderAddressHex} isn't available through the supplied web3 provider`, ); }, + isOneOfExpectedSignatureTypes(signature: string, signatureTypes: SignatureType[]): void { + sharedAssert.isHexString('signature', signature); + const signatureTypeIndexIfExists = utils.getSignatureTypeIndexIfExists(signature); + const isExpectedSignatureType = _.includes(signatureTypes, signatureTypeIndexIfExists); + if (!isExpectedSignatureType) { + throw new Error( + `Unexpected signatureType: ${signatureTypeIndexIfExists}. Valid signature types: ${signatureTypes}`, + ); + } + }, }; diff --git a/packages/contracts/src/utils/asset_proxy_utils.ts b/packages/order-utils/src/asset_proxy_utils.ts index a17d4cdfa..55f2d56df 100644 --- a/packages/contracts/src/utils/asset_proxy_utils.ts +++ b/packages/order-utils/src/asset_proxy_utils.ts @@ -1,8 +1,10 @@ +import { AssetProxyId, ERC20ProxyData, ERC721ProxyData, ProxyData } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import BN = require('bn.js'); import ethUtil = require('ethereumjs-util'); -import { AssetProxyId, ERC20ProxyData, ERC721ProxyData, ProxyData } from './types'; +const ERC20_PROXY_METADATA_BYTE_LENGTH = 21; +const ERC721_PROXY_METADATA_BYTE_LENGTH = 53; export const assetProxyUtils = { encodeAssetProxyId(assetProxyId: AssetProxyId): Buffer { @@ -26,8 +28,10 @@ export const assetProxyUtils = { return address; }, encodeUint256(value: BigNumber): Buffer { - const formattedValue = new BN(value.toString(10)); + const base = 10; + const formattedValue = new BN(value.toString(base)); const encodedValue = ethUtil.toBuffer(formattedValue); + // tslint:disable-next-line:custom-no-magic-numbers const paddedValue = ethUtil.setLengthLeft(encodedValue, 32); return paddedValue; }, @@ -45,7 +49,7 @@ export const assetProxyUtils = { }, decodeERC20ProxyData(proxyData: string): ERC20ProxyData { const encodedProxyMetadata = ethUtil.toBuffer(proxyData); - if (encodedProxyMetadata.byteLength !== 21) { + if (encodedProxyMetadata.byteLength !== ERC20_PROXY_METADATA_BYTE_LENGTH) { throw new Error( `Could not decode ERC20 Proxy Data. Expected length of encoded data to be 21. Got ${ encodedProxyMetadata.byteLength @@ -61,7 +65,8 @@ export const assetProxyUtils = { }), but got ${assetProxyId}`, ); } - const encodedTokenAddress = encodedProxyMetadata.slice(0, 20); + const addressOffset = ERC20_PROXY_METADATA_BYTE_LENGTH - 1; + const encodedTokenAddress = encodedProxyMetadata.slice(0, addressOffset); const tokenAddress = assetProxyUtils.decodeAddress(encodedTokenAddress); const erc20ProxyData = { assetProxyId, @@ -79,7 +84,7 @@ export const assetProxyUtils = { }, decodeERC721ProxyData(proxyData: string): ERC721ProxyData { const encodedProxyMetadata = ethUtil.toBuffer(proxyData); - if (encodedProxyMetadata.byteLength !== 53) { + if (encodedProxyMetadata.byteLength !== ERC721_PROXY_METADATA_BYTE_LENGTH) { throw new Error( `Could not decode ERC20 Proxy Data. Expected length of encoded data to be 53. Got ${ encodedProxyMetadata.byteLength @@ -95,9 +100,11 @@ export const assetProxyUtils = { }), but got ${assetProxyId}`, ); } - const encodedTokenAddress = encodedProxyMetadata.slice(0, 20); + const addressOffset = ERC20_PROXY_METADATA_BYTE_LENGTH - 1; + const encodedTokenAddress = encodedProxyMetadata.slice(0, addressOffset); const tokenAddress = assetProxyUtils.decodeAddress(encodedTokenAddress); - const encodedTokenId = encodedProxyMetadata.slice(20, 52); + const tokenIdOffset = ERC721_PROXY_METADATA_BYTE_LENGTH - 1; + const encodedTokenId = encodedProxyMetadata.slice(addressOffset, tokenIdOffset); const tokenId = assetProxyUtils.decodeUint256(encodedTokenId); const erc721ProxyData = { assetProxyId, diff --git a/packages/contracts/src/utils/crypto.ts b/packages/order-utils/src/crypto.ts index 80c5f30a5..517ca2840 100644 --- a/packages/contracts/src/utils/crypto.ts +++ b/packages/order-utils/src/crypto.ts @@ -26,7 +26,8 @@ export const crypto = { argTypes.push('uint8'); } else if (arg.isBigNumber) { argTypes.push('uint256'); - args[i] = new BN(arg.toString(10), 10); + const base = 10; + args[i] = new BN(arg.toString(base), base); } else if (ethUtil.isValidAddress(arg)) { argTypes.push('address'); } else if (_.isString(arg)) { diff --git a/packages/order-utils/src/formatters.ts b/packages/order-utils/src/formatters.ts deleted file mode 100644 index 2b6f4ddb7..000000000 --- a/packages/order-utils/src/formatters.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Order, OrderAddresses, OrderValues } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; - -export const formatters = { - getOrderAddressesAndValues(order: Order): [OrderAddresses, OrderValues] { - const orderAddresses: OrderAddresses = [ - order.maker, - order.taker, - order.makerTokenAddress, - order.takerTokenAddress, - order.feeRecipient, - ]; - const orderValues: OrderValues = [ - order.makerTokenAmount, - order.takerTokenAmount, - order.makerFee, - order.takerFee, - order.expirationUnixTimestampSec, - order.salt, - ]; - return [orderAddresses, orderValues]; - }, -}; diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts index e9cea95ed..b844fbfcb 100644 --- a/packages/order-utils/src/index.ts +++ b/packages/order-utils/src/index.ts @@ -1,11 +1,12 @@ -export { getOrderHashHex, isValidOrderHash } from './order_hash'; -export { isValidSignature, signOrderHashAsync } from './signature_utils'; +export { orderHashUtils } from './order_hash'; +export { isValidSignatureAsync, ecSignOrderHashAsync, addSignedMessagePrefix } from './signature_utils'; export { orderFactory } from './order_factory'; export { constants } from './constants'; +export { crypto } from './crypto'; export { generatePseudoRandomSalt } from './salt'; -export { OrderError } from './types'; -export { formatters } from './formatters'; +export { OrderError, MessagePrefixType, MessagePrefixOpts } 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'; diff --git a/packages/order-utils/src/order_factory.ts b/packages/order-utils/src/order_factory.ts index 2759aac81..678336ac5 100644 --- a/packages/order-utils/src/order_factory.ts +++ b/packages/order-utils/src/order_factory.ts @@ -1,49 +1,69 @@ -import { Provider, SignedOrder } from '@0xproject/types'; +import { ECSignature, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; +import { Provider } from 'ethereum-types'; +import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; -import { getOrderHashHex } from './order_hash'; +import { orderHashUtils } from './order_hash'; import { generatePseudoRandomSalt } from './salt'; -import { signOrderHashAsync } from './signature_utils'; - -const SHOULD_ADD_PERSONAL_MESSAGE_PREFIX = false; +import { ecSignOrderHashAsync } from './signature_utils'; +import { MessagePrefixType } from './types'; export const orderFactory = { async createSignedOrderAsync( provider: Provider, - maker: string, - taker: string, + makerAddress: string, + takerAddress: string, + senderAddress: string, makerFee: BigNumber, takerFee: BigNumber, - makerTokenAmount: BigNumber, - makerTokenAddress: string, - takerTokenAmount: BigNumber, - takerTokenAddress: string, - exchangeContractAddress: string, - feeRecipient: string, - expirationUnixTimestampSecIfExists?: BigNumber, + makerAssetAmount: BigNumber, + makerAssetData: string, + takerAssetAmount: BigNumber, + takerAssetData: string, + exchangeAddress: string, + feeRecipientAddress: string, + expirationTimeSecondsIfExists?: BigNumber, ): Promise<SignedOrder> { const defaultExpirationUnixTimestampSec = new BigNumber(2524604400); // Close to infinite - const expirationUnixTimestampSec = _.isUndefined(expirationUnixTimestampSecIfExists) + const expirationTimeSeconds = _.isUndefined(expirationTimeSecondsIfExists) ? defaultExpirationUnixTimestampSec - : expirationUnixTimestampSecIfExists; + : expirationTimeSecondsIfExists; const order = { - maker, - taker, + makerAddress, + takerAddress, + senderAddress, makerFee, takerFee, - makerTokenAmount, - takerTokenAmount, - makerTokenAddress, - takerTokenAddress, + makerAssetAmount, + takerAssetAmount, + makerAssetData, + takerAssetData, salt: generatePseudoRandomSalt(), - exchangeContractAddress, - feeRecipient, - expirationUnixTimestampSec, + exchangeAddress, + feeRecipientAddress, + expirationTimeSeconds, + }; + const orderHash = orderHashUtils.getOrderHashHex(order); + const messagePrefixOpts = { + prefixType: MessagePrefixType.EthSign, + shouldAddPrefixBeforeCallingEthSign: false, }; - const orderHash = getOrderHashHex(order); - const ecSignature = await signOrderHashAsync(provider, orderHash, maker, SHOULD_ADD_PERSONAL_MESSAGE_PREFIX); - const signedOrder: SignedOrder = _.assign(order, { ecSignature }); + const ecSignature = await ecSignOrderHashAsync(provider, orderHash, makerAddress, messagePrefixOpts); + const signature = getVRSHexString(ecSignature); + const signedOrder: SignedOrder = _.assign(order, { signature }); return signedOrder; }, }; + +function getVRSHexString(ecSignature: ECSignature): string { + const vrs = `0x${intToHex(ecSignature.v)}${ethUtil.stripHexPrefix(ecSignature.r)}${ethUtil.stripHexPrefix( + ecSignature.s, + )}`; + return vrs; +} + +function intToHex(i: number): string { + const hex = ethUtil.bufferToHex(ethUtil.toBuffer(i)); + return hex; +} diff --git a/packages/order-utils/src/order_hash.ts b/packages/order-utils/src/order_hash.ts index 108344a04..2ef746ef8 100644 --- a/packages/order-utils/src/order_hash.ts +++ b/packages/order-utils/src/order_hash.ts @@ -1,90 +1,117 @@ import { schemas, SchemaValidator } from '@0xproject/json-schemas'; -import { Order, SignedOrder, SolidityTypes } from '@0xproject/types'; +import { Order, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import BN = require('bn.js'); +import { SolidityTypes } from 'ethereum-types'; import * as ethABI from 'ethereumjs-abi'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; import { assert } from './assert'; +import { crypto } from './crypto'; -const INVALID_TAKER_FORMAT = 'instance.taker is not of a type(s) string'; +const INVALID_TAKER_FORMAT = 'instance.takerAddress is not of a type(s) string'; -/** - * Converts BigNumber instance to BN - * The only reason we convert to BN is to remain compatible with `ethABI.soliditySHA3` that - * expects values of Solidity type `uint` to be passed as type `BN`. - * We do not use BN anywhere else in the codebase. - */ -function bigNumberToBN(value: BigNumber): BN { - const base = 10; - return new BN(value.toString(), base); -} - -/** - * Computes the orderHash for a supplied order. - * @param order An object that conforms to the Order or SignedOrder interface definitions. - * @return The resulting orderHash from hashing the supplied order. - */ -export function getOrderHashHex(order: Order | SignedOrder): string { - try { - assert.doesConformToSchema('order', order, schemas.orderSchema); - } catch (error) { - if (_.includes(error.message, INVALID_TAKER_FORMAT)) { - const errMsg = - 'Order taker must be of type string. If you want anyone to be able to fill an order - pass ZeroEx.NULL_ADDRESS'; - throw new Error(errMsg); +export const orderHashUtils = { + /** + * Checks if the supplied hex encoded order hash is valid. + * Note: Valid means it has the expected format, not that an order with the orderHash exists. + * Use this method when processing orderHashes submitted as user input. + * @param orderHash Hex encoded orderHash. + * @return Whether the supplied orderHash has the expected format. + */ + isValidOrderHash(orderHash: string): boolean { + // Since this method can be called to check if any arbitrary string conforms to an orderHash's + // format, we only assert that we were indeed passed a string. + assert.isString('orderHash', orderHash); + const schemaValidator = new SchemaValidator(); + const isValid = schemaValidator.validate(orderHash, schemas.orderHashSchema).valid; + return isValid; + }, + /** + * Computes the orderHash for a supplied order. + * @param order An object that conforms to the Order or SignedOrder interface definitions. + * @return The resulting orderHash from hashing the supplied order. + */ + getOrderHashHex(order: SignedOrder | Order): string { + try { + assert.doesConformToSchema('order', order, schemas.orderSchema, [schemas.hexSchema]); + } catch (error) { + if (_.includes(error.message, INVALID_TAKER_FORMAT)) { + const errMsg = + 'Order taker must be of type string. If you want anyone to be able to fill an order - pass ZeroEx.NULL_ADDRESS'; + throw new Error(errMsg); + } + throw error; } - throw error; - } - const orderParts = [ - { value: order.exchangeContractAddress, type: SolidityTypes.Address }, - { value: order.maker, type: SolidityTypes.Address }, - { value: order.taker, type: SolidityTypes.Address }, - { value: order.makerTokenAddress, type: SolidityTypes.Address }, - { value: order.takerTokenAddress, type: SolidityTypes.Address }, - { value: order.feeRecipient, type: SolidityTypes.Address }, - { - value: bigNumberToBN(order.makerTokenAmount), - type: SolidityTypes.Uint256, - }, - { - value: bigNumberToBN(order.takerTokenAmount), - type: SolidityTypes.Uint256, - }, - { - value: bigNumberToBN(order.makerFee), - type: SolidityTypes.Uint256, - }, - { - value: bigNumberToBN(order.takerFee), - type: SolidityTypes.Uint256, - }, - { - value: bigNumberToBN(order.expirationUnixTimestampSec), - type: SolidityTypes.Uint256, - }, - { value: bigNumberToBN(order.salt), type: SolidityTypes.Uint256 }, - ]; - const types = _.map(orderParts, o => o.type); - const values = _.map(orderParts, o => o.value); - const hashBuff = ethABI.soliditySHA3(types, values); - const hashHex = ethUtil.bufferToHex(hashBuff); - return hashHex; -} -/** - * Checks if the supplied hex encoded order hash is valid. - * Note: Valid means it has the expected format, not that an order with the orderHash exists. - * Use this method when processing orderHashes submitted as user input. - * @param orderHash Hex encoded orderHash. - * @return Whether the supplied orderHash has the expected format. - */ -export function isValidOrderHash(orderHash: string): boolean { - // Since this method can be called to check if any arbitrary string conforms to an orderHash's - // format, we only assert that we were indeed passed a string. - assert.isString('orderHash', orderHash); - const schemaValidator = new SchemaValidator(); - const isValid = schemaValidator.validate(orderHash, schemas.orderHashSchema).valid; - return isValid; -} + const orderHashBuff = this.getOrderHashBuff(order); + const orderHashHex = `0x${orderHashBuff.toString('hex')}`; + return orderHashHex; + }, + /** + * Computes the orderHash for a supplied order and returns it as a Buffer + * @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), + ]); + 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; + }, +}; diff --git a/packages/order-utils/src/order_state_utils.ts b/packages/order-utils/src/order_state_utils.ts index 36171f526..61050c9d6 100644 --- a/packages/order-utils/src/order_state_utils.ts +++ b/packages/order-utils/src/order_state_utils.ts @@ -11,7 +11,8 @@ import * as _ from 'lodash'; import { AbstractBalanceAndProxyAllowanceFetcher } from './abstract/abstract_balance_and_proxy_allowance_fetcher'; import { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_filled_cancelled_fetcher'; -import { getOrderHashHex } from './order_hash'; +import { assetProxyUtils } from './asset_proxy_utils'; +import { orderHashUtils } from './order_hash'; import { RemainingFillableCalculator } from './remaining_fillable_calculator'; const ACCEPTABLE_RELATIVE_ROUNDING_ERROR = 0.0001; @@ -23,7 +24,7 @@ export class OrderStateUtils { const unavailableTakerTokenAmount = orderRelevantState.cancelledTakerTokenAmount.add( orderRelevantState.filledTakerTokenAmount, ); - const availableTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount); + const availableTakerTokenAmount = signedOrder.takerAssetAmount.minus(unavailableTakerTokenAmount); if (availableTakerTokenAmount.eq(0)) { throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero); } @@ -42,9 +43,9 @@ export class OrderStateUtils { throw new Error(ExchangeContractErrs.InsufficientMakerFeeAllowance); } } - const minFillableTakerTokenAmountWithinNoRoundingErrorRange = signedOrder.takerTokenAmount + const minFillableTakerTokenAmountWithinNoRoundingErrorRange = signedOrder.takerAssetAmount .dividedBy(ACCEPTABLE_RELATIVE_ROUNDING_ERROR) - .dividedBy(signedOrder.makerTokenAmount); + .dividedBy(signedOrder.makerAssetAmount); if ( orderRelevantState.remainingFillableTakerTokenAmount.lessThan( minFillableTakerTokenAmountWithinNoRoundingErrorRange, @@ -62,7 +63,7 @@ export class OrderStateUtils { } public async getOrderStateAsync(signedOrder: SignedOrder): Promise<OrderState> { const orderRelevantState = await this.getOrderRelevantStateAsync(signedOrder); - const orderHash = getOrderHashHex(signedOrder); + const orderHash = orderHashUtils.getOrderHashHex(signedOrder); try { OrderStateUtils._validateIfOrderIsValid(signedOrder, orderRelevantState); const orderState: OrderStateValid = { @@ -82,22 +83,22 @@ export class OrderStateUtils { } public async getOrderRelevantStateAsync(signedOrder: SignedOrder): Promise<OrderRelevantState> { const zrxTokenAddress = this._orderFilledCancelledFetcher.getZRXTokenAddress(); - const orderHash = getOrderHashHex(signedOrder); + const orderHash = orderHashUtils.getOrderHashHex(signedOrder); const makerBalance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync( - signedOrder.makerTokenAddress, - signedOrder.maker, + signedOrder.makerAssetData, + signedOrder.makerAddress, ); const makerProxyAllowance = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync( - signedOrder.makerTokenAddress, - signedOrder.maker, + signedOrder.makerAssetData, + signedOrder.makerAddress, ); const makerFeeBalance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync( zrxTokenAddress, - signedOrder.maker, + signedOrder.makerAddress, ); const makerFeeProxyAllowance = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync( zrxTokenAddress, - signedOrder.maker, + signedOrder.makerAddress, ); const filledTakerTokenAmount = await this._orderFilledCancelledFetcher.getFilledTakerAmountAsync(orderHash); const cancelledTakerTokenAmount = await this._orderFilledCancelledFetcher.getCancelledTakerAmountAsync( @@ -106,8 +107,8 @@ export class OrderStateUtils { const unavailableTakerTokenAmount = await this._orderFilledCancelledFetcher.getUnavailableTakerAmountAsync( orderHash, ); - const totalMakerTokenAmount = signedOrder.makerTokenAmount; - const totalTakerTokenAmount = signedOrder.takerTokenAmount; + const totalMakerTokenAmount = signedOrder.makerAssetAmount; + const totalTakerTokenAmount = signedOrder.takerAssetAmount; const remainingTakerTokenAmount = totalTakerTokenAmount.minus(unavailableTakerTokenAmount); const remainingMakerTokenAmount = remainingTakerTokenAmount .times(totalMakerTokenAmount) @@ -115,7 +116,8 @@ export class OrderStateUtils { const transferrableMakerTokenAmount = BigNumber.min([makerProxyAllowance, makerBalance]); const transferrableFeeTokenAmount = BigNumber.min([makerFeeProxyAllowance, makerFeeBalance]); - const isMakerTokenZRX = signedOrder.makerTokenAddress === zrxTokenAddress; + const zrxAssetData = assetProxyUtils.encodeERC20ProxyData(zrxTokenAddress); + const isMakerTokenZRX = signedOrder.makerAssetData === zrxAssetData; const remainingFillableCalculator = new RemainingFillableCalculator( signedOrder, isMakerTokenZRX, diff --git a/packages/order-utils/src/remaining_fillable_calculator.ts b/packages/order-utils/src/remaining_fillable_calculator.ts index 184c13aa4..b291d8ea9 100644 --- a/packages/order-utils/src/remaining_fillable_calculator.ts +++ b/packages/order-utils/src/remaining_fillable_calculator.ts @@ -23,7 +23,7 @@ export class RemainingFillableCalculator { this._remainingMakerTokenAmount = remainingMakerTokenAmount; this._remainingMakerFeeAmount = remainingMakerTokenAmount .times(signedOrder.makerFee) - .dividedToIntegerBy(signedOrder.makerTokenAmount); + .dividedToIntegerBy(signedOrder.makerAssetAmount); } public computeRemainingMakerFillable(): BigNumber { if (this._hasSufficientFundsForFeeAndTransferAmount()) { @@ -36,8 +36,8 @@ export class RemainingFillableCalculator { } public computeRemainingTakerFillable(): BigNumber { return this.computeRemainingMakerFillable() - .times(this._signedOrder.takerTokenAmount) - .dividedToIntegerBy(this._signedOrder.makerTokenAmount); + .times(this._signedOrder.takerAssetAmount) + .dividedToIntegerBy(this._signedOrder.makerAssetAmount); } private _hasSufficientFundsForFeeAndTransferAmount(): boolean { if (this._isMakerTokenZRX) { @@ -59,7 +59,7 @@ export class RemainingFillableCalculator { } private _calculatePartiallyFillableMakerTokenAmount(): BigNumber { // Given an order for 200 wei for 2 ZRXwei fee, find 100 wei for 1 ZRXwei. Order ratio is then 100:1 - const orderToFeeRatio = this._signedOrder.makerTokenAmount.dividedBy(this._signedOrder.makerFee); + const orderToFeeRatio = this._signedOrder.makerAssetAmount.dividedBy(this._signedOrder.makerFee); // The number of times the maker can fill the order, if each fill only required the transfer of a single // baseUnit of fee tokens. // Given 2 ZRXwei, the maximum amount of times Maker can fill this order, in terms of fees, is 2 @@ -81,10 +81,10 @@ export class RemainingFillableCalculator { // When Ratio is not fully divisible there can be remainders which cannot be represented, so they are floored. // This can result in a RoundingError being thrown by the Exchange Contract. const partiallyFillableMakerTokenAmount = fillableTimesInMakerTokenUnits - .times(this._signedOrder.makerTokenAmount) + .times(this._signedOrder.makerAssetAmount) .dividedToIntegerBy(this._signedOrder.makerFee); const partiallyFillableFeeTokenAmount = fillableTimesInFeeTokenBaseUnits - .times(this._signedOrder.makerTokenAmount) + .times(this._signedOrder.makerAssetAmount) .dividedToIntegerBy(this._signedOrder.makerFee); const partiallyFillableAmount = BigNumber.min( partiallyFillableMakerTokenAmount, diff --git a/packages/order-utils/src/signature_utils.ts b/packages/order-utils/src/signature_utils.ts index ebd636b20..c57699af0 100644 --- a/packages/order-utils/src/signature_utils.ts +++ b/packages/order-utils/src/signature_utils.ts @@ -1,28 +1,167 @@ import { schemas } from '@0xproject/json-schemas'; -import { ECSignature, Provider } from '@0xproject/types'; +import { ECSignature, SignatureType, ValidatorSignature } from '@0xproject/types'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { Provider } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; +import { artifacts } from './artifacts'; import { assert } from './assert'; -import { OrderError } from './types'; +import { ExchangeContract } from './generated_contract_wrappers/exchange'; +import { IValidatorContract } from './generated_contract_wrappers/i_validator'; +import { IWalletContract } from './generated_contract_wrappers/i_wallet'; +import { MessagePrefixOpts, MessagePrefixType, OrderError } from './types'; +import { utils } from './utils'; /** - * Verifies that the elliptic curve signature `signature` was generated - * by signing `data` with the private key corresponding to the `signerAddress` address. + * Verifies that the provided signature is valid according to the 0x Protocol smart contracts * @param data The hex encoded data signed by the supplied signature. - * @param signature An object containing the elliptic curve signature parameters. + * @param signature A hex encoded 0x Protocol signature made up of: [TypeSpecificData][SignatureType]. + * E.g [vrs][SignatureType.EIP712] * @param signerAddress The hex encoded address that signed the data, producing the supplied signature. * @return Whether the signature is valid for the supplied signerAddress and data. */ -export function isValidSignature(data: string, signature: ECSignature, signerAddress: string): boolean { +export async function isValidSignatureAsync( + provider: Provider, + data: string, + signature: string, + signerAddress: string, +): Promise<boolean> { + const signatureTypeIndexIfExists = utils.getSignatureTypeIndexIfExists(signature); + if (_.isUndefined(signatureTypeIndexIfExists)) { + throw new Error(`Unrecognized signatureType in signature: ${signature}`); + } + + switch (signatureTypeIndexIfExists) { + case SignatureType.Illegal: + case SignatureType.Invalid: + return false; + + case SignatureType.EIP712: { + const ecSignature = parseECSignature(signature); + return isValidECSignature(data, ecSignature, signerAddress); + } + + case SignatureType.EthSign: { + const ecSignature = parseECSignature(signature); + const prefixedMessageHex = addSignedMessagePrefix(data, MessagePrefixType.EthSign); + return isValidECSignature(prefixedMessageHex, ecSignature, signerAddress); + } + + case SignatureType.Caller: + // HACK: We currently do not "validate" the caller signature type. + // It can only be validated during Exchange contract execution. + throw new Error('Caller signature type cannot be validated off-chain'); + + case SignatureType.Wallet: { + const isValid = await isValidWalletSignatureAsync(provider, data, signature, signerAddress); + return isValid; + } + + case SignatureType.Validator: { + const isValid = await isValidValidatorSignatureAsync(provider, data, signature, signerAddress); + return isValid; + } + + case SignatureType.PreSigned: { + return isValidPresignedSignatureAsync(provider, data, signerAddress); + } + + case SignatureType.Trezor: { + const prefixedMessageHex = addSignedMessagePrefix(data, MessagePrefixType.Trezor); + const ecSignature = parseECSignature(signature); + return isValidECSignature(prefixedMessageHex, ecSignature, signerAddress); + } + + default: + throw new Error(`Unhandled SignatureType: ${signatureTypeIndexIfExists}`); + } +} + +/** + * Verifies that the provided presigned signature is valid according to the 0x Protocol smart contracts + * @param data The hex encoded data signed by the supplied signature. + * @param signature A hex encoded presigned 0x Protocol signature made up of: [SignatureType.Presigned] + * @param signerAddress The hex encoded address that signed the data, producing the supplied signature. + * @return Whether the data was preSigned by the supplied signerAddress. + */ +export async function isValidPresignedSignatureAsync( + provider: Provider, + data: string, + signerAddress: string, +): Promise<boolean> { + const exchangeContract = new ExchangeContract(artifacts.Exchange.abi, signerAddress, provider); + const isValid = await exchangeContract.preSigned.callAsync(data, signerAddress); + return isValid; +} + +/** + * Verifies that the provided wallet signature is valid according to the 0x Protocol smart contracts + * @param data The hex encoded data signed by the supplied signature. + * @param signature A hex encoded presigned 0x Protocol signature made up of: [SignatureType.Presigned] + * @param signerAddress The hex encoded address that signed the data, producing the supplied signature. + * @return Whether the data was preSigned by the supplied signerAddress. + */ +export async function isValidWalletSignatureAsync( + provider: Provider, + data: string, + signature: string, + signerAddress: string, +): Promise<boolean> { + // tslint:disable-next-line:custom-no-magic-numbers + const signatureWithoutType = signature.slice(-2); + const walletContract = new IWalletContract(artifacts.IWallet.abi, signerAddress, provider); + const isValid = await walletContract.isValidSignature.callAsync(data, signatureWithoutType); + return isValid; +} + +/** + * Verifies that the provided validator signature is valid according to the 0x Protocol smart contracts + * @param data The hex encoded data signed by the supplied signature. + * @param signature A hex encoded presigned 0x Protocol signature made up of: [SignatureType.Presigned] + * @param signerAddress The hex encoded address that signed the data, producing the supplied signature. + * @return Whether the data was preSigned by the supplied signerAddress. + */ +export async function isValidValidatorSignatureAsync( + provider: Provider, + data: string, + signature: string, + signerAddress: string, +): Promise<boolean> { + const validatorSignature = parseValidatorSignature(signature); + const exchangeContract = new ExchangeContract(artifacts.Exchange.abi, signerAddress, provider); + const isValidatorApproved = await exchangeContract.allowedValidators.callAsync( + signerAddress, + validatorSignature.validatorAddress, + ); + if (!isValidatorApproved) { + throw new Error(`Validator ${validatorSignature.validatorAddress} was not pre-approved by ${signerAddress}.`); + } + + const validatorContract = new IValidatorContract(artifacts.IValidator.abi, signerAddress, provider); + const isValid = await validatorContract.isValidSignature.callAsync( + data, + signerAddress, + validatorSignature.signature, + ); + return isValid; +} + +/** + * Checks if the supplied elliptic curve signature corresponds to signing `data` with + * the private key corresponding to `signerAddress` + * @param data The hex encoded data signed by the supplied signature. + * @param signature An object containing the elliptic curve signature parameters. + * @param signerAddress The hex encoded address that signed the data, producing the supplied signature. + * @return Whether the ECSignature is valid. + */ +export function isValidECSignature(data: string, signature: ECSignature, signerAddress: string): boolean { assert.isHexString('data', data); assert.doesConformToSchema('signature', signature, schemas.ecSignatureSchema); assert.isETHAddressHex('signerAddress', signerAddress); const normalizedSignerAddress = signerAddress.toLowerCase(); - const dataBuff = ethUtil.toBuffer(data); - const msgHashBuff = ethUtil.hashPersonalMessage(dataBuff); + const msgHashBuff = ethUtil.toBuffer(data); try { const pubKey = ethUtil.ecrecover( msgHashBuff, @@ -36,23 +175,23 @@ export function isValidSignature(data: string, signature: ECSignature, signerAdd return false; } } + /** * Signs an orderHash and returns it's elliptic curve signature. * This method currently supports TestRPC, Geth and Parity above and below V1.6.6 * @param orderHash Hex encoded orderHash 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. - * @param shouldAddPersonalMessagePrefix Some signers add the personal message prefix `\x19Ethereum Signed Message` - * themselves (e.g Parity Signer, Ledger, TestRPC) and others expect it to already be done by the client - * (e.g Metamask). Depending on which signer this request is going to, decide on whether to add the prefix - * before sending the request. + * @param hashPrefixOpts Different signers add/require different prefixes be appended to the message being signed. + * Since we cannot know ahead of time which signer you are using, you must supply both a prefixType and + * whether it must be added before calling `eth_sign` (some signers add it themselves) * @return An object containing the Elliptic curve signature parameters generated by signing the orderHash. */ -export async function signOrderHashAsync( +export async function ecSignOrderHashAsync( provider: Provider, orderHash: string, signerAddress: string, - shouldAddPersonalMessagePrefix: boolean, + messagePrefixOpts: MessagePrefixOpts, ): Promise<ECSignature> { assert.isHexString('orderHash', orderHash); const web3Wrapper = new Web3Wrapper(provider); @@ -60,12 +199,10 @@ export async function signOrderHashAsync( const normalizedSignerAddress = signerAddress.toLowerCase(); let msgHashHex = orderHash; - if (shouldAddPersonalMessagePrefix) { - const orderHashBuff = ethUtil.toBuffer(orderHash); - const msgHashBuff = ethUtil.hashPersonalMessage(orderHashBuff); - msgHashHex = ethUtil.bufferToHex(msgHashBuff); + const prefixedMsgHashHex = addSignedMessagePrefix(orderHash, messagePrefixOpts.prefixType); + if (messagePrefixOpts.shouldAddPrefixBeforeCallingEthSign) { + msgHashHex = prefixedMsgHashHex; } - const signature = await web3Wrapper.signMessageAsync(normalizedSignerAddress, msgHashHex); // HACK: There is no consensus on whether the signatureHex string should be formatted as @@ -76,7 +213,7 @@ export async function signOrderHashAsync( const validVParamValues = [27, 28]; const ecSignatureVRS = parseSignatureHexAsVRS(signature); if (_.includes(validVParamValues, ecSignatureVRS.v)) { - const isValidVRSSignature = isValidSignature(orderHash, ecSignatureVRS, normalizedSignerAddress); + const isValidVRSSignature = isValidECSignature(prefixedMsgHashHex, ecSignatureVRS, normalizedSignerAddress); if (isValidVRSSignature) { return ecSignatureVRS; } @@ -84,7 +221,7 @@ export async function signOrderHashAsync( const ecSignatureRSV = parseSignatureHexAsRSV(signature); if (_.includes(validVParamValues, ecSignatureRSV.v)) { - const isValidRSVSignature = isValidSignature(orderHash, ecSignatureRSV, normalizedSignerAddress); + const isValidRSVSignature = isValidECSignature(prefixedMsgHashHex, ecSignatureRSV, normalizedSignerAddress); if (isValidRSVSignature) { return ecSignatureRSV; } @@ -93,6 +230,64 @@ export async function signOrderHashAsync( throw new Error(OrderError.InvalidSignature); } +/** + * Adds the relevant prefix to the message being signed. + * @param message Message to sign + * @param messagePrefixType The type of message prefix to add. Different signers expect + * specific message prefixes. + * @return Prefixed message + */ +export function addSignedMessagePrefix(message: string, messagePrefixType: MessagePrefixType): string { + switch (messagePrefixType) { + case MessagePrefixType.None: + return message; + + case MessagePrefixType.EthSign: { + const msgBuff = ethUtil.toBuffer(message); + const prefixedMsgBuff = ethUtil.hashPersonalMessage(msgBuff); + const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff); + return prefixedMsgHex; + } + + case MessagePrefixType.Trezor: { + const msgBuff = ethUtil.toBuffer(message); + const prefixedMsgBuff = hashTrezorPersonalMessage(msgBuff); + const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff); + return prefixedMsgHex; + } + + default: + throw new Error(`Unrecognized MessagePrefixType: ${messagePrefixType}`); + } +} + +function hashTrezorPersonalMessage(message: Buffer): Buffer { + const prefix = ethUtil.toBuffer('\x19Ethereum Signed Message:\n' + String.fromCharCode(message.length)); + return ethUtil.sha3(Buffer.concat([prefix, message])); +} + +function parseECSignature(signature: string): ECSignature { + const ecSignatureTypes = [SignatureType.EthSign, SignatureType.EIP712, SignatureType.Trezor]; + assert.isOneOfExpectedSignatureTypes(signature, ecSignatureTypes); + + // tslint:disable-next-line:custom-no-magic-numbers + const vrsHex = signature.slice(0, -2); + const ecSignature = parseSignatureHexAsVRS(vrsHex); + + return ecSignature; +} + +function parseValidatorSignature(signature: string): ValidatorSignature { + assert.isOneOfExpectedSignatureTypes(signature, [SignatureType.Validator]); + // tslint:disable:custom-no-magic-numbers + const validatorSignature = { + validatorAddress: signature.slice(-22, -2), + signature: signature.slice(0, -22), + }; + // tslint:enable:custom-no-magic-numbers + return validatorSignature; +} + function parseSignatureHexAsVRS(signatureHex: string): ECSignature { const signatureBuffer = ethUtil.toBuffer(signatureHex); let v = signatureBuffer[0]; diff --git a/packages/order-utils/src/types.ts b/packages/order-utils/src/types.ts index f79d52359..db0bfb249 100644 --- a/packages/order-utils/src/types.ts +++ b/packages/order-utils/src/types.ts @@ -1,3 +1,25 @@ export enum OrderError { InvalidSignature = 'INVALID_SIGNATURE', } + +/** + * The requisite message prefix (is any) to add to an `eth_sign` request. + */ +export enum MessagePrefixType { + None = 'NONE', + EthSign = 'ETH_SIGN', + Trezor = 'TREZOR', +} + +/** + * Options related to message prefixing of messages sent to `eth_sign` + * Some signers prepend a message prefix (e.g Parity Signer, Ledger, TestRPC), while + * others require it already be prepended (e.g Metamask). In addition, different signers + * expect slightly different prefixes (See: https://github.com/ethereum/go-ethereum/issues/14794). + * Depending on the signer that will receive your signing request, you must specify the + * desired prefix and whether it should be added before making the `eth_sign` request. + */ +export interface MessagePrefixOpts { + prefixType: MessagePrefixType; + shouldAddPrefixBeforeCallingEthSign: boolean; +} diff --git a/packages/order-utils/src/utils.ts b/packages/order-utils/src/utils.ts new file mode 100644 index 000000000..3b465cece --- /dev/null +++ b/packages/order-utils/src/utils.ts @@ -0,0 +1,9 @@ +export const utils = { + getSignatureTypeIndexIfExists(signature: string): number { + // tslint:disable-next-line:custom-no-magic-numbers + const signatureTypeHex = signature.slice(-2); + const base = 16; + const signatureTypeInt = parseInt(signatureTypeHex, base); + return signatureTypeInt; + }, +}; diff --git a/packages/order-utils/test/order_hash_test.ts b/packages/order-utils/test/order_hash_test.ts index db5489509..d571fc62a 100644 --- a/packages/order-utils/test/order_hash_test.ts +++ b/packages/order-utils/test/order_hash_test.ts @@ -1,10 +1,11 @@ import { web3Factory } from '@0xproject/dev-utils'; +import { Order } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; import 'make-promises-safe'; import 'mocha'; -import { constants, getOrderHashHex } from '../src'; +import { constants, orderHashUtils } from '../src'; import { chaiSetup } from './utils/chai_setup'; import { web3Wrapper } from './utils/web3_wrapper'; @@ -14,34 +15,50 @@ const expect = chai.expect; describe('Order hashing', () => { describe('#getOrderHashHex', () => { - const expectedOrderHash = '0x39da987067a3c9e5f1617694f1301326ba8c8b0498ebef5df4863bed394e3c83'; + const expectedOrderHash = '0x367ad7730eb8b5feab8a9c9f47c6fcba77a2d4df125ee6a59cc26ac955710f7e'; const fakeExchangeContractAddress = '0xb69e673309512a9d726f87304c6984054f87a93b'; - const order = { - maker: constants.NULL_ADDRESS, - taker: constants.NULL_ADDRESS, - feeRecipient: constants.NULL_ADDRESS, - makerTokenAddress: constants.NULL_ADDRESS, - takerTokenAddress: constants.NULL_ADDRESS, - exchangeContractAddress: fakeExchangeContractAddress, + const order: Order = { + makerAddress: constants.NULL_ADDRESS, + 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), - makerTokenAmount: new BigNumber(0), - takerTokenAmount: new BigNumber(0), - expirationUnixTimestampSec: new BigNumber(0), + makerAssetAmount: new BigNumber(0), + takerAssetAmount: new BigNumber(0), + expirationTimeSeconds: new BigNumber(0), }; it('calculates the order hash', async () => { - const orderHash = getOrderHashHex(order); + const orderHash = orderHashUtils.getOrderHashHex(order); expect(orderHash).to.be.equal(expectedOrderHash); }); it('throws a readable error message if taker format is invalid', async () => { const orderWithInvalidtakerFormat = { ...order, - taker: (null as any) as string, + takerAddress: (null as any) as string, }; const expectedErrorMessage = 'Order taker must be of type string. If you want anyone to be able to fill an order - pass ZeroEx.NULL_ADDRESS'; - expect(() => getOrderHashHex(orderWithInvalidtakerFormat)).to.throw(expectedErrorMessage); + expect(() => orderHashUtils.getOrderHashHex(orderWithInvalidtakerFormat)).to.throw(expectedErrorMessage); + }); + }); + describe('#isValidOrderHash', () => { + it('returns false if the value is not a hex string', () => { + const isValid = orderHashUtils.isValidOrderHash('not a hex'); + expect(isValid).to.be.false(); + }); + it('returns false if the length is wrong', () => { + const isValid = orderHashUtils.isValidOrderHash('0xdeadbeef'); + expect(isValid).to.be.false(); + }); + it('returns true if order hash is correct', () => { + const orderHashLength = 65; + const isValid = orderHashUtils.isValidOrderHash('0x' + Array(orderHashLength).join('0')); + expect(isValid).to.be.true(); }); }); }); diff --git a/packages/order-utils/test/signature_utils_test.ts b/packages/order-utils/test/signature_utils_test.ts index e24fa0ce5..c1a3cc7fb 100644 --- a/packages/order-utils/test/signature_utils_test.ts +++ b/packages/order-utils/test/signature_utils_test.ts @@ -1,14 +1,14 @@ import { web3Factory } from '@0xproject/dev-utils'; -import { JSONRPCErrorCallback, JSONRPCRequestPayload } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; +import { JSONRPCErrorCallback, JSONRPCRequestPayload } from 'ethereum-types'; import * as _ from 'lodash'; import 'make-promises-safe'; import 'mocha'; import * as Sinon from 'sinon'; -import { generatePseudoRandomSalt, isValidOrderHash, isValidSignature, signOrderHashAsync } from '../src'; -import * as signatureUtils from '../src/signature_utils'; +import { ecSignOrderHashAsync, generatePseudoRandomSalt, MessagePrefixType, orderHashUtils } from '../src'; +import { isValidECSignature, isValidSignatureAsync } from '../src/signature_utils'; import { chaiSetup } from './utils/chai_setup'; import { provider, web3Wrapper } from './utils/web3_wrapper'; @@ -16,32 +16,82 @@ import { provider, web3Wrapper } from './utils/web3_wrapper'; chaiSetup.configure(); const expect = chai.expect; -const SHOULD_ADD_PERSONAL_MESSAGE_PREFIX = false; - describe('Signature utils', () => { describe('#isValidSignature', () => { - // The Exchange smart contract `isValidSignature` method only validates orderHashes and assumes - // the length of the data is exactly 32 bytes. Thus for these tests, we use data of this size. - const dataHex = '0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0'; + let dataHex = '0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0'; + const ethSignSignature = + '0x1B61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc3340349190569279751135161d22529dc25add4f6069af05be04cacbda2ace225403'; + let address = '0x5409ed021d9299bf6814279a6a1411a7e866a631'; + + it("should return false if the data doesn't pertain to the signature & address", async () => { + expect(await isValidSignatureAsync(provider, '0x0', ethSignSignature, address)).to.be.false(); + }); + it("should return false if the address doesn't pertain to the signature & data", async () => { + const validUnrelatedAddress = '0x8b0292b11a196601ed2ce54b665cafeca0347d42'; + expect( + await isValidSignatureAsync(provider, dataHex, ethSignSignature, validUnrelatedAddress), + ).to.be.false(); + }); + it("should return false if the signature doesn't pertain to the dataHex & address", async () => { + const signatureArray = ethSignSignature.split(''); + // tslint:disable-next-line:custom-no-magic-numbers + signatureArray[5] = 'C'; // V = 28, instead of 27 + const wrongSignature = signatureArray.join(''); + expect(await isValidSignatureAsync(provider, dataHex, wrongSignature, address)).to.be.false(); + }); + + it('should throw if signatureType is invalid', () => { + const signatureArray = ethSignSignature.split(''); + signatureArray[3] = '9'; // SignatureType w/ index 9 doesn't exist + const signatureWithInvalidType = signatureArray.join(''); + expect(isValidSignatureAsync(provider, dataHex, signatureWithInvalidType, address)).to.be.rejected(); + }); + + it('should return true for a valid Ecrecover (EthSign) signature', async () => { + const isValidSignatureLocal = await isValidSignatureAsync(provider, dataHex, ethSignSignature, address); + expect(isValidSignatureLocal).to.be.true(); + }); + + it('should return true for a valid EIP712 signature', async () => { + dataHex = '0xa1d7403bcbbcd75ec233cfd6584ff8dabed677d0e9bb32c2bea94e9dd8a109da'; + address = '0x6ecbe1db9ef729cbe972c83fb886247691fb6beb'; + const eip712Signature = + '0x1bdde07aac4bf12c12ddbb155919c43eba4146a2cfcf904a862950dbebe332554c6674975603eb5a4eaf8fd7f2e06350267e5b36cda9851a89f8bb49fe2fc9afe202'; + const isValidSignatureLocal = await isValidSignatureAsync(provider, dataHex, eip712Signature, address); + expect(isValidSignatureLocal).to.be.true(); + }); + + it('should return true for a valid Trezor signature', async () => { + dataHex = '0xd0d994e31c88f33fd8a572552a70ed339de579e5ba49ee1d17cc978bbe1cdd21'; + address = '0x6ecbe1db9ef729cbe972c83fb886247691fb6beb'; + const trezorSignature = + '0x1ce4760660e6495b5ae6723087bea073b3a99ce98ea81fdf00c240279c010e63d05b87bc34c4d67d4776e8d5aeb023a67484f4eaf0fd353b40893e5101e845cd9908'; + const isValidSignatureLocal = await isValidSignatureAsync(provider, dataHex, trezorSignature, address); + expect(isValidSignatureLocal).to.be.true(); + }); + }); + describe('#isValidECSignature', () => { const signature = { v: 27, - r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33', - s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254', + r: '0xaca7da997ad177f040240cdccf6905b71ab16b74434388c3a72f34fd25d64393', + s: '0x46b2bac274ff29b48b3ea6e2d04c1336eaceafda3c53ab483fc3ff12fac3ebf2', }; - const address = '0x5409ed021d9299bf6814279a6a1411a7e866a631'; + const data = '0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad'; + const address = '0x0e5cb767cce09a7f3ca594df118aa519be5e2b5a'; + it("should return false if the data doesn't pertain to the signature & address", async () => { - expect(isValidSignature('0x0', signature, address)).to.be.false(); + expect(isValidECSignature('0x0', signature, address)).to.be.false(); }); it("should return false if the address doesn't pertain to the signature & data", async () => { const validUnrelatedAddress = '0x8b0292b11a196601ed2ce54b665cafeca0347d42'; - expect(isValidSignature(dataHex, signature, validUnrelatedAddress)).to.be.false(); + expect(isValidECSignature(data, signature, validUnrelatedAddress)).to.be.false(); }); - it("should return false if the signature doesn't pertain to the dataHex & address", async () => { + it("should return false if the signature doesn't pertain to the data & address", async () => { const wrongSignature = _.assign({}, signature, { v: 28 }); - expect(isValidSignature(dataHex, wrongSignature, address)).to.be.false(); + expect(isValidECSignature(data, wrongSignature, address)).to.be.false(); }); - it('should return true if the signature does pertain to the dataHex & address', async () => { - const isValidSignatureLocal = isValidSignature(dataHex, signature, address); + it('should return true if the signature does pertain to the data & address', async () => { + const isValidSignatureLocal = isValidECSignature(data, signature, address); expect(isValidSignatureLocal).to.be.true(); }); }); @@ -58,22 +108,7 @@ describe('Signature utils', () => { expect(salt.lessThan(twoPow256)).to.be.true(); }); }); - describe('#isValidOrderHash', () => { - it('returns false if the value is not a hex string', () => { - const isValid = isValidOrderHash('not a hex'); - expect(isValid).to.be.false(); - }); - it('returns false if the length is wrong', () => { - const isValid = isValidOrderHash('0xdeadbeef'); - expect(isValid).to.be.false(); - }); - it('returns true if order hash is correct', () => { - const orderHashLength = 65; - const isValid = isValidOrderHash('0x' + Array(orderHashLength).join('0')); - expect(isValid).to.be.true(); - }); - }); - describe('#signOrderHashAsync', () => { + describe('#ecSignOrderHashAsync', () => { let stubs: Sinon.SinonStub[] = []; let makerAddress: string; before(async () => { @@ -92,12 +127,11 @@ describe('Signature utils', () => { r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33', s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254', }; - const ecSignature = await signOrderHashAsync( - provider, - orderHash, - makerAddress, - SHOULD_ADD_PERSONAL_MESSAGE_PREFIX, - ); + const messagePrefixOpts = { + prefixType: MessagePrefixType.EthSign, + shouldAddPrefixBeforeCallingEthSign: false, + }; + const ecSignature = await ecSignOrderHashAsync(provider, orderHash, makerAddress, messagePrefixOpts); expect(ecSignature).to.deep.equal(expectedECSignature); }); it('should return the correct ECSignature for signatureHex concatenated as R + S + V', async () => { @@ -126,12 +160,11 @@ describe('Signature utils', () => { }, }; - const ecSignature = await signOrderHashAsync( - fakeProvider, - orderHash, - makerAddress, - SHOULD_ADD_PERSONAL_MESSAGE_PREFIX, - ); + const messagePrefixOpts = { + prefixType: MessagePrefixType.EthSign, + shouldAddPrefixBeforeCallingEthSign: false, + }; + const ecSignature = await ecSignOrderHashAsync(fakeProvider, orderHash, makerAddress, messagePrefixOpts); expect(ecSignature).to.deep.equal(expectedECSignature); }); it('should return the correct ECSignature for signatureHex concatenated as V + R + S', async () => { @@ -157,12 +190,11 @@ describe('Signature utils', () => { }, }; - const ecSignature = await signOrderHashAsync( - fakeProvider, - orderHash, - makerAddress, - SHOULD_ADD_PERSONAL_MESSAGE_PREFIX, - ); + const messagePrefixOpts = { + prefixType: MessagePrefixType.EthSign, + shouldAddPrefixBeforeCallingEthSign: false, + }; + const ecSignature = await ecSignOrderHashAsync(fakeProvider, orderHash, makerAddress, messagePrefixOpts); expect(ecSignature).to.deep.equal(expectedECSignature); }); }); diff --git a/packages/order-utils/test/utils/web3_wrapper.ts b/packages/order-utils/test/utils/web3_wrapper.ts index 71a0dc1c2..419f76dde 100644 --- a/packages/order-utils/test/utils/web3_wrapper.ts +++ b/packages/order-utils/test/utils/web3_wrapper.ts @@ -1,6 +1,6 @@ import { devConstants, web3Factory } from '@0xproject/dev-utils'; -import { Provider } from '@0xproject/types'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { Provider } from 'ethereum-types'; const provider: Provider = web3Factory.getRpcProvider({ shouldUseInProcessGanache: true }); const web3Wrapper = new Web3Wrapper(provider); diff --git a/packages/order-watcher/package.json b/packages/order-watcher/package.json index 8d9283978..fcc40d56d 100644 --- a/packages/order-watcher/package.json +++ b/packages/order-watcher/package.json @@ -81,7 +81,7 @@ "@0xproject/contract-wrappers": "^0.0.2", "@0xproject/fill-scenarios": "^0.0.2", "@0xproject/json-schemas": "0.7.22", - "@0xproject/order-utils": "^0.0.5", + "@0xproject/order-utils": "0.0.5", "@0xproject/types": "0.7.0", "@0xproject/typescript-typings": "^0.3.2", "@0xproject/utils": "^0.6.2", diff --git a/packages/sra-report/package.json b/packages/sra-report/package.json index cc34b5ced..8903f5c81 100644 --- a/packages/sra-report/package.json +++ b/packages/sra-report/package.json @@ -33,7 +33,7 @@ "dependencies": { "@0xproject/assert": "^0.2.10", "@0xproject/types": "0.7.0", - "@0xproject/order-utils": "^0.0.5", + "@0xproject/order-utils": "0.0.5", "@0xproject/connect": "0.6.12", "@0xproject/json-schemas": "0.7.22", "@0xproject/typescript-typings": "^0.3.2", diff --git a/packages/types/package.json b/packages/types/package.json index 376eb1d68..95b7721d3 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/types", - "version": "0.7.1", + "version": "1.0.0", "engines": { "node": ">=6.12" }, diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 47f3e3239..5a4d23a9e 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,6 +1,9 @@ import { BigNumber } from 'bignumber.js'; import { ContractAbi, DecodedLogArgs, LogEntry, LogWithDecodedArgs, TransactionReceipt } from 'ethereum-types'; +// HACK: Rather then extending from OrderWithoutExchangeAddress +// we don't, because our docs don't expand inherited types, and it's unnecessarily +// confusing to introduce the user to the OrderWithoutExchangeAddress type. export interface Order { senderAddress: string; makerAddress: string; @@ -12,16 +15,28 @@ export interface Order { makerAssetData: string; takerAssetData: string; salt: BigNumber; + exchangeAddress: string; feeRecipientAddress: string; expirationTimeSeconds: BigNumber; } -export interface SignedOrder extends UnsignedOrder { - signature: string; +export interface OrderWithoutExchangeAddress { + senderAddress: string; + makerAddress: string; + takerAddress: string; + makerFee: BigNumber; + takerFee: BigNumber; + makerAssetAmount: BigNumber; + takerAssetAmount: BigNumber; + makerAssetData: string; + takerAssetData: string; + salt: BigNumber; + feeRecipientAddress: string; + expirationTimeSeconds: BigNumber; } -export interface UnsignedOrder extends Order { - exchangeAddress: string; +export interface SignedOrder extends Order { + signature: string; } /** @@ -34,6 +49,14 @@ export interface ECSignature { } /** + * Validator signature components + */ +export interface ValidatorSignature { + validatorAddress: string; + signature: string; +} + +/** * Errors originating from the 0x exchange contract */ export enum ExchangeContractErrs { @@ -106,3 +129,47 @@ export interface Token { symbol: string; decimals: number; } + +export enum SignatureType { + Illegal, + Invalid, + EIP712, + EthSign, + Caller, + Wallet, + Validator, + PreSigned, + Trezor, +} + +/** + * Elliptic Curve signature + */ +export interface ECSignature { + v: number; + r: string; + s: string; +} + +export enum AssetProxyId { + INVALID, + ERC20, + ERC721, +} + +export interface ERC20ProxyData { + assetProxyId: AssetProxyId; + tokenAddress: string; +} + +export interface ERC721ProxyData { + assetProxyId: AssetProxyId; + tokenAddress: string; + tokenId: BigNumber; +} + +export interface ProxyData { + assetProxyId: AssetProxyId; + tokenAddress?: string; + data?: any; +} |