Add utils for hashing and signing orders, update wrappers

This commit is contained in:
Amir Bandeali
2018-03-07 16:05:43 -08:00
parent cd8f8e1e4a
commit 3ff8a319c5
6 changed files with 96 additions and 53 deletions

View File

@@ -19,7 +19,7 @@
pragma solidity ^0.4.19;
pragma experimental ABIEncoderV2;
import './mixins/MExchangeCore.sol';
import "./mixins/MExchangeCore.sol";
import "../../utils/SafeMath/SafeMath.sol";
/// @dev Consumes MExchangeCore

View File

@@ -8,7 +8,7 @@ import { ExchangeContract } from '../contract_wrappers/generated/exchange';
import { constants } from './constants';
import { formatters } from './formatters';
import { LogDecoder } from './log_decoder';
import { signedOrderUtils } from './signed_order_utils';
import { orderUtils } from './order_utils';
import { SignedOrder } from './types';
export class ExchangeWrapper {
@@ -24,7 +24,7 @@ export class ExchangeWrapper {
from: string,
opts: { takerTokenFillAmount?: BigNumber } = {},
): Promise<TransactionReceiptWithDecodedLogs> {
const params = signedOrderUtils.createFill(signedOrder, opts.takerTokenFillAmount);
const params = orderUtils.createFill(signedOrder, opts.takerTokenFillAmount);
const txHash = await this._exchange.fillOrder.sendTransactionAsync(
params.order,
params.takerTokenFillAmount,
@@ -45,7 +45,7 @@ export class ExchangeWrapper {
from: string,
opts: { takerTokenCancelAmount?: BigNumber } = {},
): Promise<TransactionReceiptWithDecodedLogs> {
const params = signedOrderUtils.createCancel(signedOrder, opts.takerTokenCancelAmount);
const params = orderUtils.createCancel(signedOrder, opts.takerTokenCancelAmount);
const txHash = await this._exchange.cancelOrder.sendTransactionAsync(
params.order,
params.takerTokenCancelAmount,
@@ -65,7 +65,7 @@ export class ExchangeWrapper {
from: string,
opts: { takerTokenFillAmount?: BigNumber } = {},
): Promise<TransactionReceiptWithDecodedLogs> {
const params = signedOrderUtils.createFill(signedOrder, opts.takerTokenFillAmount);
const params = orderUtils.createFill(signedOrder, opts.takerTokenFillAmount);
const txHash = await this._exchange.fillOrKillOrder.sendTransactionAsync(
params.order,
params.takerTokenFillAmount,
@@ -165,14 +165,14 @@ export class ExchangeWrapper {
return tx;
}
public async getOrderHashAsync(signedOrder: SignedOrder): Promise<string> {
const order = signedOrderUtils.getOrderStruct(signedOrder);
const order = orderUtils.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(
orderUtils.getOrderHashHex(signedOrder),
signedOrder.makerAddress,
signedOrderUtils.getOrderHashHex(signedOrder),
signedOrder.signature,
);
return isValidSignature;
@@ -195,6 +195,10 @@ export class ExchangeWrapper {
);
return partialAmount;
}
public async getFilledTakerTokenAmountAsync(orderHashHex: string): Promise<BigNumber> {
const filledAmount = new BigNumber(await this._exchange.filled.callAsync(orderHashHex));
return filledAmount;
}
}
function wrapLogBigNumbers(log: any): any {

View File

@@ -1,37 +1,35 @@
import { Order, ZeroEx } from '0x.js';
import { ZeroEx } from '0x.js';
import { BigNumber } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as _ from 'lodash';
import { signedOrderUtils } from './signed_order_utils';
import { DefaultOrderParams, SignedOrder } from './types';
import { orderUtils } from './order_utils';
import { signingUtils } from './signing_utils';
import { DefaultOrderParams, SignatureType, SignedOrder, UnsignedOrder } from './types';
export class OrderFactory {
private _defaultOrderParams: Partial<Order>;
private _zeroEx: ZeroEx;
constructor(zeroEx: ZeroEx, defaultOrderParams: Partial<Order>) {
private _defaultOrderParams: Partial<UnsignedOrder>;
private _secretKey: Buffer;
constructor(secretKey: Buffer, defaultOrderParams: Partial<UnsignedOrder>) {
this._defaultOrderParams = defaultOrderParams;
this._zeroEx = zeroEx;
this._secretKey = secretKey;
}
public async newSignedOrderAsync(customOrderParams: Partial<Order> = {}): Promise<SignedOrder> {
public newSignedOrder(
customOrderParams: Partial<UnsignedOrder> = {},
signatureType: SignatureType = SignatureType.Ecrecover,
): SignedOrder {
const randomExpiration = new BigNumber(Math.floor((Date.now() + Math.random() * 100000000000) / 1000));
const order = ({
expirationTimestampSeconds: randomExpiration,
expirationTimeSeconds: randomExpiration,
salt: ZeroEx.generatePseudoRandomSalt(),
takerAddress: ZeroEx.NULL_ADDRESS,
...this._defaultOrderParams,
...customOrderParams,
} as any) as SignedOrder;
const orderHashHex = signedOrderUtils.getOrderHashHex(order);
const shouldAddPersonalMessagePrefix = false;
const ecSignature = await this._zeroEx.signOrderHashAsync(
orderHashHex,
order.makerAddress,
shouldAddPersonalMessagePrefix,
);
} as any) as UnsignedOrder;
const orderHashBuff = orderUtils.getOrderHashBuff(order);
const signature = signingUtils.signMessage(orderHashBuff, this._secretKey, signatureType);
const signedOrder = {
...order,
ecSignature,
signature: `0x${signature.toString('hex')}`,
};
return signedOrder;
}

View File

@@ -4,12 +4,12 @@ import ethUtil = require('ethereumjs-util');
import * as _ from 'lodash';
import { crypto } from './crypto';
import { OrderStruct, SignatureType, SignedOrder } from './types';
import { OrderStruct, SignatureType, SignedOrder, UnsignedOrder } from './types';
export const signedOrderUtils = {
export const orderUtils = {
createFill: (signedOrder: SignedOrder, takerTokenFillAmount?: BigNumber) => {
const fill = {
order: signedOrderUtils.getOrderStruct(signedOrder),
order: orderUtils.getOrderStruct(signedOrder),
takerTokenFillAmount: takerTokenFillAmount || signedOrder.takerTokenAmount,
signature: signedOrder.signature,
};
@@ -17,7 +17,7 @@ export const signedOrderUtils = {
},
createCancel(signedOrder: SignedOrder, takerTokenCancelAmount?: BigNumber) {
const cancel = {
order: signedOrderUtils.getOrderStruct(signedOrder),
order: orderUtils.getOrderStruct(signedOrder),
takerTokenCancelAmount: takerTokenCancelAmount || signedOrder.takerTokenAmount,
};
return cancel;
@@ -38,7 +38,7 @@ export const signedOrderUtils = {
};
return orderStruct;
},
getOrderHashHex(signedOrder: SignedOrder): string {
getOrderHashBuff(order: SignedOrder | UnsignedOrder): Buffer {
const orderSchemaHashBuff = crypto.solSHA3([
'address exchangeAddress',
'address makerAddress',
@@ -50,35 +50,31 @@ export const signedOrderUtils = {
'uint256 takerTokenAmount',
'uint256 makerFeeAmount',
'uint256 takerFeeAmount',
'uint256 expirationTimestampSeconds',
'uint256 expirationTimeSeconds',
'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,
order.exchangeAddress,
order.makerAddress,
order.takerAddress,
order.makerTokenAddress,
order.takerTokenAddress,
order.feeRecipientAddress,
order.makerTokenAmount,
order.takerTokenAmount,
order.makerFeeAmount,
order.takerFeeAmount,
order.expirationTimeSeconds,
order.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;
return prefixedOrderHashBuff;
},
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;
getOrderHashHex(order: SignedOrder | UnsignedOrder): string {
const orderHashBuff = orderUtils.getOrderHashBuff(order);
const orderHashHex = `0x${orderHashBuff.toString('hex')}`;
return orderHashHex;
},
};

View File

@@ -0,0 +1,30 @@
import * as ethUtil from 'ethereumjs-util';
import { SignatureType } from './types';
export const signingUtils = {
signMessage(message: Buffer, secretKey: Buffer, signatureType: SignatureType): Buffer {
if (signatureType === SignatureType.Ecrecover) {
const prefixedMessage = ethUtil.hashPersonalMessage(message);
const ecSignature = ethUtil.ecsign(prefixedMessage, secretKey);
const signature = Buffer.concat([
ethUtil.toBuffer(signatureType),
ethUtil.toBuffer(ecSignature.v),
ecSignature.r,
ecSignature.s,
]);
return signature;
} else if (signatureType === SignatureType.EIP712) {
const ecSignature = ethUtil.ecsign(message, secretKey);
const signature = Buffer.concat([
ethUtil.toBuffer(signatureType),
ethUtil.toBuffer(ecSignature.v),
ecSignature.r,
ecSignature.s,
]);
return signature;
} else {
throw new Error(`${signatureType} is not a valid signature type`);
}
},
};

View File

@@ -138,6 +138,21 @@ export interface OrderStruct {
salt: BigNumber;
}
export interface UnsignedOrder {
exchangeAddress: string;
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,