diff options
Diffstat (limited to 'packages')
48 files changed, 773 insertions, 495 deletions
diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json index e4f7a1d03..9efb1c972 100644 --- a/packages/0x.js/package.json +++ b/packages/0x.js/package.json @@ -101,7 +101,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 9185ab46f..b28d04685 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 7bd0cf571..8d29366a4 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -67,7 +67,7 @@ }, "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/typescript-typings": "^0.3.2", 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 ca587f220..8f5915d97 100644 --- a/packages/contracts/src/utils/exchange_wrapper.ts +++ b/packages/contracts/src/utils/exchange_wrapper.ts @@ -1,4 +1,4 @@ -import { Provider, SignedOrder, TransactionReceiptWithDecodedLogs } from '@0xproject/types'; +import { AssetProxyId, Provider, SignedOrder, TransactionReceiptWithDecodedLogs } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as _ from 'lodash'; @@ -9,7 +9,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 87399b9f6..85348b14d 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 { LogWithDecodedArgs, SignedOrder } from '@0xproject/types'; +import { assetProxyUtils, crypto, orderHashUtils } from '@0xproject/order-utils'; +import { AssetProxyId, LogWithDecodedArgs, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; import ethUtil = require('ethereumjs-util'); @@ -14,17 +15,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, @@ -122,7 +119,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 @@ -130,7 +127,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 @@ -181,7 +178,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 @@ -192,7 +189,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 f704c26ec..59fc663f4 100644 --- a/packages/contracts/src/utils/order_factory.ts +++ b/packages/contracts/src/utils/order_factory.ts @@ -1,22 +1,21 @@ import { generatePseudoRandomSalt } from '@0xproject/order-utils'; -import { SignedOrder, UnsignedOrder } from '@0xproject/types'; +import { 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> = {}, + customOrderParams: Partial<Order> = {}, signatureType: SignatureType = SignatureType.Ecrecover, ): SignedOrder { const randomExpiration = new BigNumber(Math.floor((Date.now() + Math.random() * 100000000000) / 1000)); @@ -27,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..d56d2cd44 100644 --- a/packages/contracts/src/utils/signing_utils.ts +++ b/packages/contracts/src/utils/signing_utils.ts @@ -1,7 +1,6 @@ +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) { diff --git a/packages/contracts/src/utils/transaction_factory.ts b/packages/contracts/src/utils/transaction_factory.ts index 65cdb3f89..f66d55f9b 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; diff --git a/packages/contracts/src/utils/types.ts b/packages/contracts/src/utils/types.ts index 1eeffc70e..a6c90dae0 100644 --- a/packages/contracts/src/utils/types.ts +++ b/packages/contracts/src/utils/types.ts @@ -1,4 +1,4 @@ -import { AbiDefinition, ContractAbi, Order } from '@0xproject/types'; +import { AbiDefinition, ContractAbi, Order, OrderWithoutExchangeAddress } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; export interface ERC20BalancesByOwner { @@ -18,37 +18,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[]; @@ -114,18 +108,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; @@ -161,31 +143,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 bc476a5ee..adc57c7a4 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 { LogWithDecodedArgs, SignedOrder } from '@0xproject/types'; +import { assetProxyUtils, crypto, orderHashUtils } from '@0xproject/order-utils'; +import { AssetProxyId, LogWithDecodedArgs, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as chai from 'chai'; @@ -17,16 +18,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(); @@ -127,7 +125,6 @@ describe('Exchange core', () => { afterEach(async () => { await blockchainLifecycle.revertAsync(); }); - describe('fillOrder', () => { beforeEach(async () => { erc20Balances = await erc20Wrapper.getBalancesAsync(); @@ -141,7 +138,7 @@ describe('Exchange core', () => { }); const takerAssetFilledAmountBefore = await exchangeWrapper.getTakerAssetFilledAmountAsync( - orderUtils.getOrderHashHex(signedOrder), + orderHashUtils.getOrderHashHex(signedOrder), ); expect(takerAssetFilledAmountBefore).to.be.bignumber.equal(0); @@ -151,7 +148,7 @@ describe('Exchange core', () => { }); const takerAssetFilledAmountAfter1 = await exchangeWrapper.getTakerAssetFilledAmountAsync( - orderUtils.getOrderHashHex(signedOrder), + orderHashUtils.getOrderHashHex(signedOrder), ); expect(takerAssetFilledAmountAfter1).to.be.bignumber.equal(fillTakerAssetAmount1); @@ -161,7 +158,7 @@ describe('Exchange core', () => { }); const takerAssetFilledAmountAfter2 = await exchangeWrapper.getTakerAssetFilledAmountAsync( - orderUtils.getOrderHashHex(signedOrder), + orderHashUtils.getOrderHashHex(signedOrder), ); expect(takerAssetFilledAmountAfter2).to.be.bignumber.equal(takerAssetFilledAmountAfter1); }); @@ -173,7 +170,7 @@ describe('Exchange core', () => { }); const takerAssetFilledAmountBefore = await exchangeWrapper.getTakerAssetFilledAmountAsync( - orderUtils.getOrderHashHex(signedOrder), + orderHashUtils.getOrderHashHex(signedOrder), ); expect(takerAssetFilledAmountBefore).to.be.bignumber.equal(0); @@ -181,7 +178,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); @@ -226,7 +223,7 @@ describe('Exchange core', () => { }); const takerAssetFilledAmountBefore = await exchangeWrapper.getTakerAssetFilledAmountAsync( - orderUtils.getOrderHashHex(signedOrder), + orderHashUtils.getOrderHashHex(signedOrder), ); expect(takerAssetFilledAmountBefore).to.be.bignumber.equal(0); @@ -234,7 +231,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); @@ -279,7 +276,7 @@ describe('Exchange core', () => { }); const takerAssetFilledAmountBefore = await exchangeWrapper.getTakerAssetFilledAmountAsync( - orderUtils.getOrderHashHex(signedOrder), + orderHashUtils.getOrderHashHex(signedOrder), ); expect(takerAssetFilledAmountBefore).to.be.bignumber.equal(0); @@ -287,7 +284,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); @@ -333,7 +330,7 @@ describe('Exchange core', () => { }); const takerAssetFilledAmountBefore = await exchangeWrapper.getTakerAssetFilledAmountAsync( - orderUtils.getOrderHashHex(signedOrder), + orderHashUtils.getOrderHashHex(signedOrder), ); expect(takerAssetFilledAmountBefore).to.be.bignumber.equal(0); @@ -341,7 +338,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); @@ -441,7 +438,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 () => { @@ -535,12 +532,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 () => { @@ -556,12 +547,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 () => { @@ -652,7 +637,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 0da0287bc..67281a915 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 { LogWithDecodedArgs, SignedOrder } from '@0xproject/types'; +import { assetProxyUtils, crypto } from '@0xproject/order-utils'; +import { AssetProxyId, LogWithDecodedArgs, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as chai from 'chai'; @@ -17,17 +18,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, @@ -36,8 +34,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..194053abc 100644 --- a/packages/contracts/test/exchange/signature_validator.ts +++ b/packages/contracts/test/exchange/signature_validator.ts @@ -1,4 +1,6 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; +import { orderHashUtils } from '@0xproject/order-utils'; +import { assetProxyUtils } from '@0xproject/order-utils'; import { SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; @@ -7,11 +9,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 +64,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 +81,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 6b3083ae5..ef7595562 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, SignatureType, 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..d8055908a 100644 --- a/packages/contracts/test/exchange/wrapper.ts +++ b/packages/contracts/test/exchange/wrapper.ts @@ -1,5 +1,5 @@ import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils'; -import { SignedOrder } from '@0xproject/types'; +import { AssetProxyId, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as chai from 'chai'; @@ -14,14 +14,14 @@ 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 { assetProxyUtils } from '@0xproject/order-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 e817951ab..32b319e56 100644 --- a/packages/contracts/test/libraries/lib_bytes.ts +++ b/packages/contracts/test/libraries/lib_bytes.ts @@ -1,5 +1,5 @@ import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils'; -import { LogWithDecodedArgs, TransactionReceiptWithDecodedLogs } from '@0xproject/types'; +import { AssetProxyId, LogWithDecodedArgs, TransactionReceiptWithDecodedLogs } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import BN = require('bn.js'); @@ -11,7 +11,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 f74c2672d..e2f1a2af1 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", "@0xproject/typescript-typings": "^0.3.2", "@0xproject/utils": "^0.6.2", 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/artifacts/2.0.0/ISigner.json b/packages/migrations/artifacts/2.0.0/ISigner.json new file mode 100644 index 000000000..8a0a8e06a --- /dev/null +++ b/packages/migrations/artifacts/2.0.0/ISigner.json @@ -0,0 +1,76 @@ +{ + "schemaVersion": "2.0.0", + "contractName": "ISigner", + "compilerOutput": { + "abi": [ + { + "constant": true, + "inputs": [ + { + "name": "hash", + "type": "bytes32" + }, + { + "name": "signature", + "type": "bytes" + } + ], + "name": "isValidSignature", + "outputs": [ + { + "name": "isValid", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } + ], + "evm": { + "bytecode": { + "linkReferences": {}, + "object": "0x", + "opcodes": "", + "sourceMap": "" + }, + "deployedBytecode": { + "linkReferences": {}, + "object": "0x", + "opcodes": "", + "sourceMap": "" + } + } + }, + "sources": { + "current/protocol/Exchange/interfaces/ISigner.sol": { + "id": 0 + } + }, + "sourceCodes": { + "current/protocol/Exchange/interfaces/ISigner.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity ^0.4.24;\n\ncontract ISigner {\n\n /// @dev Verifies that a signature is valid.\n /// @param hash Message hash that is signed.\n /// @param signature Proof of signing.\n /// @return Validity of order signature.\n function isValidSignature(\n bytes32 hash,\n bytes signature)\n external\n view\n returns (bool isValid);\n}\n" + }, + "sourceTreeHashHex": "0x14f56e10a8ab94bb23561e6d2118d4ea6f68154b66e0833ebd915c0911073267", + "compiler": { + "name": "solc", + "version": "soljson-v0.4.24+commit.e67f0147.js", + "settings": { + "optimizer": { + "enabled": true, + "runs": 0 + }, + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode.object", + "evm.bytecode.sourceMap", + "evm.deployedBytecode.object", + "evm.deployedBytecode.sourceMap" + ] + } + } + } + }, + "networks": {} +}
\ No newline at end of file diff --git a/packages/migrations/compiler.json b/packages/migrations/compiler.json index e1204b2d1..782b939dc 100644 --- a/packages/migrations/compiler.json +++ b/packages/migrations/compiler.json @@ -1,5 +1,5 @@ { - "artifactsDir": "artifacts/1.0.0", + "artifactsDir": "artifacts/2.0.0", "contractsDir": "../contracts/src/contracts", "contracts": [ "Exchange_v1", @@ -10,7 +10,8 @@ "MultiSigWallet", "MultiSigWalletWithTimeLock", "MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress", - "TokenRegistry" + "TokenRegistry", + "ISigner" ], "compilerSettings": { "outputSelection": { diff --git a/packages/migrations/package.json b/packages/migrations/package.json index 54866110e..ebef2dfb4 100644 --- a/packages/migrations/package.json +++ b/packages/migrations/package.json @@ -30,7 +30,7 @@ "v1": "artifacts/1.0.0/@(DummyERC20Token|TokenTransferProxy_v1|Exchange_v1|TokenRegistry|MultiSigWallet|MultiSigWalletWithTimeLock|MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress|TokenRegistry|ZRXToken|WETH9).json", "v2": - "artifacts/2.0.0/@(DummyERC20Token|DummyERC721Token|ERC20Proxy|ERC721Proxy|Exchange|MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress|ZRXToken|WETH9).json" + "artifacts/2.0.0/@(DummyERC20Token|DummyERC721Token|ERC20Proxy|ERC721Proxy|Exchange|MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress|ZRXToken|WETH9|ISigner).json" } }, "license": "Apache-2.0", diff --git a/packages/order-utils/package.json b/packages/order-utils/package.json index 3586c4523..0b0686954 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,16 @@ "types": "lib/src/index.d.ts", "scripts": { "watch": "tsc -w", + "prebuild": "run-s clean update_artifacts generate_contract_wrappers", "build": "tsc && copyfiles -u 3 './lib/src/monorepo_scripts/**/*' ./scripts", - "test": "run-s clean build run_mocha", + "generate_contract_wrappers": "abi-gen --abis 'lib/src/artifacts/@(Exchange|ISigner).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 +26,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": "ISigner Exchange", "postpublish": { "docPublishConfigs": { "extraFileIncludes": [ @@ -63,8 +67,10 @@ }, "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": "0.7.24", + "@0xproject/sol-compiler": "^0.5.0", + "@0xproject/types": "^0.7.1", "@0xproject/typescript-typings": "^0.3.2", "@0xproject/utils": "^0.6.2", "@0xproject/web3-wrapper": "^0.6.4", @@ -72,6 +78,7 @@ "bn.js": "^4.11.8", "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..6eb5ad0c0 --- /dev/null +++ b/packages/order-utils/src/artifacts.ts @@ -0,0 +1,8 @@ +import { Artifact } from '@0xproject/types'; + +import * as Exchange from './artifacts/Exchange.json'; +import * as ISigner from './artifacts/ISigner.json'; +export const artifacts = { + Exchange: (Exchange as any) as Artifact, + ISigner: (ISigner as any) as Artifact, +}; diff --git a/packages/order-utils/src/assert.ts b/packages/order-utils/src/assert.ts index 5ac402e7e..07cde453a 100644 --- a/packages/order-utils/src/assert.ts +++ b/packages/order-utils/src/assert.ts @@ -8,8 +8,6 @@ import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as _ from 'lodash'; -import { isValidSignature } from './signature_utils'; - export const assert = { ...sharedAssert, async isSenderAddressAsync( diff --git a/packages/contracts/src/utils/asset_proxy_utils.ts b/packages/order-utils/src/asset_proxy_utils.ts index a17d4cdfa..5a084deba 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 = 20; +const ERC721_PROXY_METADATA_BYTE_LENGTH = 52; 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,7 @@ export const assetProxyUtils = { }), but got ${assetProxyId}`, ); } - const encodedTokenAddress = encodedProxyMetadata.slice(0, 20); + const encodedTokenAddress = encodedProxyMetadata.slice(0, ERC20_PROXY_METADATA_BYTE_LENGTH); const tokenAddress = assetProxyUtils.decodeAddress(encodedTokenAddress); const erc20ProxyData = { assetProxyId, @@ -79,7 +83,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 +99,10 @@ export const assetProxyUtils = { }), but got ${assetProxyId}`, ); } - const encodedTokenAddress = encodedProxyMetadata.slice(0, 20); + const addressOffset = 20; + const encodedTokenAddress = encodedProxyMetadata.slice(0, addressOffset); const tokenAddress = assetProxyUtils.decodeAddress(encodedTokenAddress); - const encodedTokenId = encodedProxyMetadata.slice(20, 52); + const encodedTokenId = encodedProxyMetadata.slice(addressOffset, ERC721_PROXY_METADATA_BYTE_LENGTH); 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..3f3dc524c 100644 --- a/packages/order-utils/src/order_factory.ts +++ b/packages/order-utils/src/order_factory.ts @@ -1,49 +1,68 @@ -import { Provider, SignedOrder } from '@0xproject/types'; +import { ECSignature, Provider, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; +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..a4e36ab89 100644 --- a/packages/order-utils/src/order_hash.ts +++ b/packages/order-utils/src/order_hash.ts @@ -1,5 +1,5 @@ import { schemas, SchemaValidator } from '@0xproject/json-schemas'; -import { Order, SignedOrder, SolidityTypes } from '@0xproject/types'; +import { Order, SignatureType, SignedOrder, SolidityTypes } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import BN = require('bn.js'); import * as ethABI from 'ethereumjs-abi'; @@ -7,84 +7,110 @@ 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..8e387a6ae 100644 --- a/packages/order-utils/src/signature_utils.ts +++ b/packages/order-utils/src/signature_utils.ts @@ -1,28 +1,111 @@ import { schemas } from '@0xproject/json-schemas'; -import { ECSignature, Provider } from '@0xproject/types'; +import { ECSignature, Provider, SignatureType } from '@0xproject/types'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; 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 { ISignerContract } from './generated_contract_wrappers/i_signer'; +import { MessagePrefixOpts, MessagePrefixType, OrderError } from './types'; /** - * 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: [SignatureType][TypeSpecificData]. + * E.g [SignatureType.EIP712][vrs] * @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 = 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 signerContract = new ISignerContract(artifacts.ISigner.abi, signerAddress, provider); + const isValid = await signerContract.isValidSignature.callAsync(data, signature); + return isValid; + } + + // TODO: Add SignatureType.Validator + + case SignatureType.PreSigned: { + return isValidPresignedSignatureAsync(provider, data, signature, 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, + signature: string, + signerAddress: string, +): Promise<boolean> { + const exchangeContract = new ExchangeContract(artifacts.Exchange.abi, signerAddress, provider); + const isValid = await exchangeContract.preSigned.callAsync(data, signerAddress); + 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 +119,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 +143,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 +157,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 +165,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 +174,60 @@ export async function signOrderHashAsync( throw new Error(OrderError.InvalidSignature); } +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 { + assert.isHexString('signature', signature); + const signatureTypeIndexIfExists = getSignatureTypeIndexIfExists(signature); + const ecSignatureTypes = [SignatureType.EthSign, SignatureType.EIP712, SignatureType.Trezor]; + const isECSignatureType = _.includes(ecSignatureTypes, signatureTypeIndexIfExists); + if (!isECSignatureType) { + throw new Error(`Cannot parse non-ECSignature type: ${signatureTypeIndexIfExists}`); + } + + // tslint:disable-next-line:custom-no-magic-numbers + const vrsHex = signature.slice(0, -2); + const ecSignature = parseSignatureHexAsVRS(vrsHex); + + return ecSignature; +} + +function getSignatureTypeIndexIfExists(signature: string): number { + const unprefixedSignature = ethUtil.stripHexPrefix(signature); + // tslint:disable-next-line:custom-no-magic-numbers + const signatureTypeHex = unprefixedSignature.slice(-2); + const base = 16; + const signatureTypeInt = parseInt(signatureTypeHex, base); + return signatureTypeInt; +} + 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/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..d7d895204 100644 --- a/packages/order-utils/test/signature_utils_test.ts +++ b/packages/order-utils/test/signature_utils_test.ts @@ -7,8 +7,8 @@ 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,85 @@ 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(); + }); + + // TODO: remaining sigs + }); + describe('#isValidECSignature', () => { + // TODO: Replace this with a vanilla signature without ANY prefix or modification 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 +111,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 +130,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 +163,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 +193,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-watcher/package.json b/packages/order-watcher/package.json index a1aed439b..6b8165116 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/src/index.ts b/packages/types/src/index.ts index 7831f580e..bb8fe896d 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -292,16 +292,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; } /** @@ -386,3 +398,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; +} |