diff options
-rw-r--r-- | packages/contracts/src/contracts/current/protocol/Exchange/LibOrder.sol | 1 | ||||
-rw-r--r-- | packages/contracts/src/utils/crypto.ts | 2 | ||||
-rw-r--r-- | packages/contracts/src/utils/exchange_wrapper.ts | 56 | ||||
-rw-r--r-- | packages/contracts/src/utils/formatters.ts | 116 | ||||
-rw-r--r-- | packages/contracts/src/utils/order_factory.ts | 15 | ||||
-rw-r--r-- | packages/contracts/src/utils/signed_order_utils.ts | 82 | ||||
-rw-r--r-- | packages/contracts/src/utils/types.ts | 67 |
7 files changed, 189 insertions, 150 deletions
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/LibOrder.sol b/packages/contracts/src/contracts/current/protocol/Exchange/LibOrder.sol index 3cb76741a..55246eaea 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/LibOrder.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/LibOrder.sol @@ -36,7 +36,6 @@ contract LibOrder { "uint256 salt" ); - // TODO: Append `Address` to all address fields and `Amount` to all value fields? struct Order { address makerAddress; address takerAddress; diff --git a/packages/contracts/src/utils/crypto.ts b/packages/contracts/src/utils/crypto.ts index 810072d2f..4126b8ff4 100644 --- a/packages/contracts/src/utils/crypto.ts +++ b/packages/contracts/src/utils/crypto.ts @@ -3,6 +3,8 @@ import ABI = require('ethereumjs-abi'); import ethUtil = require('ethereumjs-util'); import * as _ from 'lodash'; +import { SignedOrder } from './types'; + export const crypto = { /** * We convert types from JS to Solidity as follows: diff --git a/packages/contracts/src/utils/exchange_wrapper.ts b/packages/contracts/src/utils/exchange_wrapper.ts index 788bf07f4..7383f6b5d 100644 --- a/packages/contracts/src/utils/exchange_wrapper.ts +++ b/packages/contracts/src/utils/exchange_wrapper.ts @@ -1,4 +1,4 @@ -import { SignedOrder, TransactionReceiptWithDecodedLogs, ZeroEx } from '0x.js'; +import { TransactionReceiptWithDecodedLogs, ZeroEx } from '0x.js'; import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; import * as Web3 from 'web3'; @@ -9,6 +9,7 @@ import { constants } from './constants'; import { formatters } from './formatters'; import { LogDecoder } from './log_decoder'; import { signedOrderUtils } from './signed_order_utils'; +import { SignedOrder } from './types'; export class ExchangeWrapper { private _exchange: ExchangeContract; @@ -25,12 +26,9 @@ export class ExchangeWrapper { ): Promise<TransactionReceiptWithDecodedLogs> { const params = signedOrderUtils.createFill(signedOrder, opts.takerTokenFillAmount); const txHash = await this._exchange.fillOrder.sendTransactionAsync( - params.orderAddresses, - params.orderValues, + params.order, params.takerTokenFillAmount, - params.v, - params.r, - params.s, + params.signature, { from }, ); const tx = await this._zeroEx.awaitTransactionMinedAsync(txHash); @@ -49,8 +47,7 @@ export class ExchangeWrapper { ): Promise<TransactionReceiptWithDecodedLogs> { const params = signedOrderUtils.createCancel(signedOrder, opts.takerTokenCancelAmount); const txHash = await this._exchange.cancelOrder.sendTransactionAsync( - params.orderAddresses, - params.orderValues, + params.order, params.takerTokenCancelAmount, { from }, ); @@ -70,12 +67,9 @@ export class ExchangeWrapper { ): Promise<TransactionReceiptWithDecodedLogs> { const params = signedOrderUtils.createFill(signedOrder, opts.takerTokenFillAmount); const txHash = await this._exchange.fillOrKillOrder.sendTransactionAsync( - params.orderAddresses, - params.orderValues, + params.order, params.takerTokenFillAmount, - params.v, - params.r, - params.s, + params.signature, { from }, ); const tx = await this._zeroEx.awaitTransactionMinedAsync(txHash); @@ -94,12 +88,9 @@ export class ExchangeWrapper { ): Promise<TransactionReceiptWithDecodedLogs> { const params = formatters.createBatchFill(orders, opts.takerTokenFillAmounts); const txHash = await this._exchange.batchFillOrders.sendTransactionAsync( - params.orderAddresses, - params.orderValues, + params.orders, params.takerTokenFillAmounts, - params.v, - params.r, - params.s, + params.signatures, { from }, ); const tx = await this._zeroEx.awaitTransactionMinedAsync(txHash); @@ -118,12 +109,9 @@ export class ExchangeWrapper { ): Promise<TransactionReceiptWithDecodedLogs> { const params = formatters.createBatchFill(orders, opts.takerTokenFillAmounts); const txHash = await this._exchange.batchFillOrKillOrders.sendTransactionAsync( - params.orderAddresses, - params.orderValues, + params.orders, params.takerTokenFillAmounts, - params.v, - params.r, - params.s, + params.signatures, { from }, ); const tx = await this._zeroEx.awaitTransactionMinedAsync(txHash); @@ -142,12 +130,9 @@ export class ExchangeWrapper { ): Promise<TransactionReceiptWithDecodedLogs> { const params = formatters.createMarketFillOrders(orders, opts.takerTokenFillAmount); const txHash = await this._exchange.marketFillOrders.sendTransactionAsync( - params.orderAddresses, - params.orderValues, + params.orders, params.takerTokenFillAmount, - params.v, - params.r, - params.s, + params.signatures, { from }, ); const tx = await this._zeroEx.awaitTransactionMinedAsync(txHash); @@ -166,8 +151,7 @@ export class ExchangeWrapper { ): Promise<TransactionReceiptWithDecodedLogs> { const params = formatters.createBatchCancel(orders, opts.takerTokenCancelAmounts); const txHash = await this._exchange.batchCancelOrders.sendTransactionAsync( - params.orderAddresses, - params.orderValues, + params.orders, params.takerTokenCancelAmounts, { from }, ); @@ -181,17 +165,15 @@ export class ExchangeWrapper { return tx; } public async getOrderHashAsync(signedOrder: SignedOrder): Promise<string> { - const params = signedOrderUtils.getOrderAddressesAndValues(signedOrder); - const orderHash = await this._exchange.getOrderHash.callAsync(params.orderAddresses, params.orderValues); + const order = signedOrderUtils.getOrderStruct(signedOrder); + const orderHash = await this._exchange.getOrderHash.callAsync(order); return orderHash; } public async isValidSignatureAsync(signedOrder: SignedOrder): Promise<boolean> { const isValidSignature = await this._exchange.isValidSignature.callAsync( - signedOrder.maker, - ZeroEx.getOrderHashHex(signedOrder), - signedOrder.ecSignature.v, - signedOrder.ecSignature.r, - signedOrder.ecSignature.s, + signedOrder.makerAddress, + signedOrderUtils.getOrderHashHex(signedOrder), + signedOrder.signature, ); return isValidSignature; } diff --git a/packages/contracts/src/utils/formatters.ts b/packages/contracts/src/utils/formatters.ts index 48c77fea1..4f6116f06 100644 --- a/packages/contracts/src/utils/formatters.ts +++ b/packages/contracts/src/utils/formatters.ts @@ -1,38 +1,30 @@ -import { SignedOrder } from '0x.js'; import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; -import { BatchCancelOrders, BatchFillOrders, MarketFillOrders } from './types'; +import { BatchCancelOrders, BatchFillOrders, MarketFillOrders, SignedOrder } from './types'; export const formatters = { createBatchFill(signedOrders: SignedOrder[], takerTokenFillAmounts: BigNumber[] = []) { const batchFill: BatchFillOrders = { - orderAddresses: [], - orderValues: [], + orders: [], + signatures: [], takerTokenFillAmounts, - v: [], - r: [], - s: [], }; _.forEach(signedOrders, signedOrder => { - batchFill.orderAddresses.push([ - signedOrder.maker, - signedOrder.taker, - signedOrder.makerTokenAddress, - signedOrder.takerTokenAddress, - signedOrder.feeRecipient, - ]); - batchFill.orderValues.push([ - signedOrder.makerTokenAmount, - signedOrder.takerTokenAmount, - signedOrder.makerFee, - signedOrder.takerFee, - signedOrder.expirationUnixTimestampSec, - signedOrder.salt, - ]); - batchFill.v.push(signedOrder.ecSignature.v); - batchFill.r.push(signedOrder.ecSignature.r); - batchFill.s.push(signedOrder.ecSignature.s); + batchFill.orders.push({ + makerAddress: signedOrder.makerAddress, + takerAddress: signedOrder.takerAddress, + makerTokenAddress: signedOrder.makerTokenAddress, + takerTokenAddress: signedOrder.takerTokenAddress, + feeRecipientAddress: signedOrder.feeRecipientAddress, + makerTokenAmount: signedOrder.makerTokenAmount, + takerTokenAmount: signedOrder.takerTokenAmount, + makerFeeAmount: signedOrder.makerFeeAmount, + takerFeeAmount: signedOrder.takerFeeAmount, + expirationTimeSeconds: signedOrder.expirationTimeSeconds, + salt: signedOrder.salt, + }); + batchFill.signatures.push(signedOrder.signature); if (takerTokenFillAmounts.length < signedOrders.length) { batchFill.takerTokenFillAmounts.push(signedOrder.takerTokenAmount); } @@ -41,57 +33,47 @@ export const formatters = { }, createMarketFillOrders(signedOrders: SignedOrder[], takerTokenFillAmount: BigNumber) { const marketFillOrders: MarketFillOrders = { - orderAddresses: [], - orderValues: [], + orders: [], + signatures: [], takerTokenFillAmount, - v: [], - r: [], - s: [], }; - signedOrders.forEach(signedOrder => { - marketFillOrders.orderAddresses.push([ - signedOrder.maker, - signedOrder.taker, - signedOrder.makerTokenAddress, - signedOrder.takerTokenAddress, - signedOrder.feeRecipient, - ]); - marketFillOrders.orderValues.push([ - signedOrder.makerTokenAmount, - signedOrder.takerTokenAmount, - signedOrder.makerFee, - signedOrder.takerFee, - signedOrder.expirationUnixTimestampSec, - signedOrder.salt, - ]); - marketFillOrders.v.push(signedOrder.ecSignature.v); - marketFillOrders.r.push(signedOrder.ecSignature.r); - marketFillOrders.s.push(signedOrder.ecSignature.s); + _.forEach(signedOrders, signedOrder => { + marketFillOrders.orders.push({ + makerAddress: signedOrder.makerAddress, + takerAddress: signedOrder.takerAddress, + makerTokenAddress: signedOrder.makerTokenAddress, + takerTokenAddress: signedOrder.takerTokenAddress, + feeRecipientAddress: signedOrder.feeRecipientAddress, + makerTokenAmount: signedOrder.makerTokenAmount, + takerTokenAmount: signedOrder.takerTokenAmount, + makerFeeAmount: signedOrder.makerFeeAmount, + takerFeeAmount: signedOrder.takerFeeAmount, + expirationTimeSeconds: signedOrder.expirationTimeSeconds, + salt: signedOrder.salt, + }); + marketFillOrders.signatures.push(signedOrder.signature); }); return marketFillOrders; }, createBatchCancel(signedOrders: SignedOrder[], takerTokenCancelAmounts: BigNumber[] = []) { const batchCancel: BatchCancelOrders = { - orderAddresses: [], - orderValues: [], + orders: [], takerTokenCancelAmounts, }; - signedOrders.forEach(signedOrder => { - batchCancel.orderAddresses.push([ - signedOrder.maker, - signedOrder.taker, - signedOrder.makerTokenAddress, - signedOrder.takerTokenAddress, - signedOrder.feeRecipient, - ]); - batchCancel.orderValues.push([ - signedOrder.makerTokenAmount, - signedOrder.takerTokenAmount, - signedOrder.makerFee, - signedOrder.takerFee, - signedOrder.expirationUnixTimestampSec, - signedOrder.salt, - ]); + _.forEach(signedOrders, signedOrder => { + batchCancel.orders.push({ + makerAddress: signedOrder.makerAddress, + takerAddress: signedOrder.takerAddress, + makerTokenAddress: signedOrder.makerTokenAddress, + takerTokenAddress: signedOrder.takerTokenAddress, + feeRecipientAddress: signedOrder.feeRecipientAddress, + makerTokenAmount: signedOrder.makerTokenAmount, + takerTokenAmount: signedOrder.takerTokenAmount, + makerFeeAmount: signedOrder.makerFeeAmount, + takerFeeAmount: signedOrder.takerFeeAmount, + expirationTimeSeconds: signedOrder.expirationTimeSeconds, + salt: signedOrder.salt, + }); if (takerTokenCancelAmounts.length < signedOrders.length) { batchCancel.takerTokenCancelAmounts.push(signedOrder.takerTokenAmount); } diff --git a/packages/contracts/src/utils/order_factory.ts b/packages/contracts/src/utils/order_factory.ts index 8ba5df24a..d5c3a9544 100644 --- a/packages/contracts/src/utils/order_factory.ts +++ b/packages/contracts/src/utils/order_factory.ts @@ -1,9 +1,10 @@ -import { Order, SignedOrder, ZeroEx } from '0x.js'; +import { Order, ZeroEx } from '0x.js'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as _ from 'lodash'; -import { DefaultOrderParams } from './types'; +import { signedOrderUtils } from './signed_order_utils'; +import { DefaultOrderParams, SignedOrder } from './types'; export class OrderFactory { private _defaultOrderParams: Partial<Order>; @@ -15,17 +16,17 @@ export class OrderFactory { public async newSignedOrderAsync(customOrderParams: Partial<Order> = {}): Promise<SignedOrder> { const randomExpiration = new BigNumber(Math.floor((Date.now() + Math.random() * 100000000000) / 1000)); const order = ({ - expirationUnixTimestampSec: randomExpiration, + expirationTimestampSeconds: randomExpiration, salt: ZeroEx.generatePseudoRandomSalt(), - taker: ZeroEx.NULL_ADDRESS, + takerAddress: ZeroEx.NULL_ADDRESS, ...this._defaultOrderParams, ...customOrderParams, - } as any) as Order; - const orderHashHex = ZeroEx.getOrderHashHex(order); + } as any) as SignedOrder; + const orderHashHex = signedOrderUtils.getOrderHashHex(order); const shouldAddPersonalMessagePrefix = false; const ecSignature = await this._zeroEx.signOrderHashAsync( orderHashHex, - order.maker, + order.makerAddress, shouldAddPersonalMessagePrefix, ); const signedOrder = { diff --git a/packages/contracts/src/utils/signed_order_utils.ts b/packages/contracts/src/utils/signed_order_utils.ts index 6122748b4..1d9096ee4 100644 --- a/packages/contracts/src/utils/signed_order_utils.ts +++ b/packages/contracts/src/utils/signed_order_utils.ts @@ -1,44 +1,84 @@ -import { SignedOrder } from '0x.js'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import ethUtil = require('ethereumjs-util'); import * as _ from 'lodash'; import { crypto } from './crypto'; +import { OrderStruct, SignatureType, SignedOrder } from './types'; export const signedOrderUtils = { createFill: (signedOrder: SignedOrder, takerTokenFillAmount?: BigNumber) => { const fill = { - ...signedOrderUtils.getOrderAddressesAndValues(signedOrder), + order: signedOrderUtils.getOrderStruct(signedOrder), takerTokenFillAmount: takerTokenFillAmount || signedOrder.takerTokenAmount, - ...signedOrder.ecSignature, + signature: signedOrder.signature, }; return fill; }, createCancel(signedOrder: SignedOrder, takerTokenCancelAmount?: BigNumber) { const cancel = { - ...signedOrderUtils.getOrderAddressesAndValues(signedOrder), + order: signedOrderUtils.getOrderStruct(signedOrder), takerTokenCancelAmount: takerTokenCancelAmount || signedOrder.takerTokenAmount, }; return cancel; }, - getOrderAddressesAndValues(signedOrder: SignedOrder) { - return { - orderAddresses: [ - signedOrder.maker, - signedOrder.taker, - signedOrder.makerTokenAddress, - signedOrder.takerTokenAddress, - signedOrder.feeRecipient, - ], - orderValues: [ - signedOrder.makerTokenAmount, - signedOrder.takerTokenAmount, - signedOrder.makerFee, - signedOrder.takerFee, - signedOrder.expirationUnixTimestampSec, - signedOrder.salt, - ], + getOrderStruct(signedOrder: SignedOrder): OrderStruct { + const orderStruct = { + makerAddress: signedOrder.makerAddress, + takerAddress: signedOrder.takerAddress, + makerTokenAddress: signedOrder.makerTokenAddress, + takerTokenAddress: signedOrder.takerTokenAddress, + feeRecipientAddress: signedOrder.feeRecipientAddress, + makerTokenAmount: signedOrder.makerTokenAmount, + takerTokenAmount: signedOrder.takerTokenAmount, + makerFeeAmount: signedOrder.makerFeeAmount, + takerFeeAmount: signedOrder.takerFeeAmount, + expirationTimeSeconds: signedOrder.expirationTimeSeconds, + salt: signedOrder.salt, }; + return orderStruct; + }, + getOrderHashHex(signedOrder: SignedOrder): string { + const orderSchemaHashBuff = crypto.solSHA3([ + 'address exchangeAddress', + 'address makerAddress', + 'address takerAddress', + 'address makerTokenAddress', + 'address takerTokenAddress', + 'address feeRecipientAddress', + 'uint256 makerTokenAmount', + 'uint256 takerTokenAmount', + 'uint256 makerFeeAmount', + 'uint256 takerFeeAmount', + 'uint256 expirationTimestampSeconds', + 'uint256 salt', + ]); + const orderSchemaHashHex = `0x${orderSchemaHashBuff.toString('hex')}`; + const orderHashBuff = crypto.solSHA3([ + signedOrder.exchangeAddress, + signedOrder.makerAddress, + signedOrder.takerAddress, + signedOrder.makerTokenAddress, + signedOrder.takerTokenAddress, + signedOrder.feeRecipientAddress, + signedOrder.makerTokenAmount, + signedOrder.takerTokenAmount, + signedOrder.makerFeeAmount, + signedOrder.takerFeeAmount, + signedOrder.expirationTimeSeconds, + signedOrder.salt, + ]); + const orderHashHex = `0x${orderHashBuff.toString('hex')}`; + const prefixedOrderHashBuff = crypto.solSHA3([new BigNumber(orderSchemaHashHex), new BigNumber(orderHashHex)]); + const prefixedOrderHashHex = `0x${prefixedOrderHashBuff.toString('hex')}`; + return prefixedOrderHashHex; + }, + getSignatureType(signature: string): SignatureType { + const signatureBuff = new Buffer(signature); + const signatureType = signatureBuff[0]; + if (!_.has(SignatureType, signatureType)) { + throw new Error(`${signatureType} is not a valid signature type`); + } + return signatureType; }, }; diff --git a/packages/contracts/src/utils/types.ts b/packages/contracts/src/utils/types.ts index 2ffc84f04..5075c7bf5 100644 --- a/packages/contracts/src/utils/types.ts +++ b/packages/contracts/src/utils/types.ts @@ -12,39 +12,32 @@ export interface SubmissionContractEventArgs { } export interface BatchFillOrders { - orderAddresses: string[][]; - orderValues: BigNumber[][]; + orders: OrderStruct[]; + signatures: string[]; takerTokenFillAmounts: BigNumber[]; - v: number[]; - r: string[]; - s: string[]; } export interface MarketFillOrders { - orderAddresses: string[][]; - orderValues: BigNumber[][]; + orders: OrderStruct[]; + signatures: string[]; takerTokenFillAmount: BigNumber; - v: number[]; - r: string[]; - s: string[]; } export interface BatchCancelOrders { - orderAddresses: string[][]; - orderValues: BigNumber[][]; + orders: OrderStruct[]; takerTokenCancelAmounts: BigNumber[]; } export interface DefaultOrderParams { - exchangeContractAddress: string; - maker: string; - feeRecipient: string; + exchangeAddress: string; + makerAddress: string; + feeRecipientAddress: string; makerTokenAddress: string; takerTokenAddress: string; makerTokenAmount: BigNumber; takerTokenAmount: BigNumber; - makerFee: BigNumber; - takerFee: BigNumber; + makerFeeAmount: BigNumber; + takerFeeAmount: BigNumber; } export interface TransactionDataParams { @@ -114,3 +107,43 @@ export interface Artifact { }; }; } + +export interface SignedOrder { + exchangeAddress: string; + makerAddress: string; + takerAddress: string; + makerTokenAddress: string; + takerTokenAddress: string; + feeRecipientAddress: string; + makerTokenAmount: BigNumber; + takerTokenAmount: BigNumber; + makerFeeAmount: BigNumber; + takerFeeAmount: BigNumber; + expirationTimeSeconds: BigNumber; + salt: BigNumber; + signature: string; +} + +export interface OrderStruct { + makerAddress: string; + takerAddress: string; + makerTokenAddress: string; + takerTokenAddress: string; + feeRecipientAddress: string; + makerTokenAmount: BigNumber; + takerTokenAmount: BigNumber; + makerFeeAmount: BigNumber; + takerFeeAmount: BigNumber; + expirationTimeSeconds: BigNumber; + salt: BigNumber; +} + +export enum SignatureType { + Illegal, + Invalid, + Caller, + Ecrecover, + EIP712, + Trezor, + Contract, +} |