aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contracts/util
diff options
context:
space:
mode:
authorAmir Bandeali <abandeali1@gmail.com>2017-11-30 14:02:43 +0800
committerAmir Bandeali <abandeali1@gmail.com>2017-11-30 23:10:18 +0800
commit4b3e0383235ca4ca0127f24c2e05543bb45a56bb (patch)
tree7a9c1888f99b1121826b09ca28185565ed47cd50 /packages/contracts/util
parentc57190dead41846809effb8823bd4e486ca72512 (diff)
downloaddexon-sol-tools-4b3e0383235ca4ca0127f24c2e05543bb45a56bb.tar
dexon-sol-tools-4b3e0383235ca4ca0127f24c2e05543bb45a56bb.tar.gz
dexon-sol-tools-4b3e0383235ca4ca0127f24c2e05543bb45a56bb.tar.bz2
dexon-sol-tools-4b3e0383235ca4ca0127f24c2e05543bb45a56bb.tar.lz
dexon-sol-tools-4b3e0383235ca4ca0127f24c2e05543bb45a56bb.tar.xz
dexon-sol-tools-4b3e0383235ca4ca0127f24c2e05543bb45a56bb.tar.zst
dexon-sol-tools-4b3e0383235ca4ca0127f24c2e05543bb45a56bb.zip
Add contracts to packages, fix most linting errors
Diffstat (limited to 'packages/contracts/util')
-rw-r--r--packages/contracts/util/artifacts.ts25
-rw-r--r--packages/contracts/util/balances.ts30
-rw-r--r--packages/contracts/util/bignumber_config.ts11
-rw-r--r--packages/contracts/util/constants.ts4
-rw-r--r--packages/contracts/util/crypto.ts38
-rw-r--r--packages/contracts/util/exchange_wrapper.ts168
-rw-r--r--packages/contracts/util/formatters.ts74
-rw-r--r--packages/contracts/util/multi_sig_wrapper.ts34
-rw-r--r--packages/contracts/util/order.ts109
-rw-r--r--packages/contracts/util/order_factory.ts24
-rw-r--r--packages/contracts/util/rpc.ts43
-rw-r--r--packages/contracts/util/token_registry_wrapper.ts56
-rw-r--r--packages/contracts/util/types.ts119
13 files changed, 735 insertions, 0 deletions
diff --git a/packages/contracts/util/artifacts.ts b/packages/contracts/util/artifacts.ts
new file mode 100644
index 000000000..8114972d0
--- /dev/null
+++ b/packages/contracts/util/artifacts.ts
@@ -0,0 +1,25 @@
+export class Artifacts {
+ public Migrations: any;
+ public TokenTransferProxy: any;
+ public TokenRegistry: any;
+ public MultiSigWalletWithTimeLock: any;
+ public Exchange: any;
+ public ZRXToken: any;
+ public DummyToken: any;
+ public EtherToken: any;
+ public MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress: any;
+ public MaliciousToken: any;
+ constructor(artifacts: any) {
+ this.Migrations = artifacts.require('Migrations');
+ this.TokenTransferProxy = artifacts.require('TokenTransferProxy');
+ this.TokenRegistry = artifacts.require('TokenRegistry');
+ this.MultiSigWalletWithTimeLock = artifacts.require('MultiSigWalletWithTimeLock');
+ this.Exchange = artifacts.require('Exchange');
+ this.ZRXToken = artifacts.require('ZRXToken');
+ this.DummyToken = artifacts.require('DummyToken');
+ this.EtherToken = artifacts.require('EtherToken');
+ this.MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress = artifacts.require(
+ 'MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress');
+ this.MaliciousToken = artifacts.require('MaliciousToken');
+ }
+}
diff --git a/packages/contracts/util/balances.ts b/packages/contracts/util/balances.ts
new file mode 100644
index 000000000..ef4abd7bf
--- /dev/null
+++ b/packages/contracts/util/balances.ts
@@ -0,0 +1,30 @@
+import {BigNumber} from 'bignumber.js';
+import * as _ from 'lodash';
+
+import {bigNumberConfigs} from './bignumber_config';
+import {BalancesByOwner, ContractInstance} from './types';
+
+bigNumberConfigs.configure();
+
+export class Balances {
+ private tokenContractInstances: ContractInstance[];
+ private ownerAddresses: string[];
+ constructor(tokenContractInstances: ContractInstance[], ownerAddresses: string[]) {
+ this.tokenContractInstances = tokenContractInstances;
+ this.ownerAddresses = ownerAddresses;
+ }
+ public async getAsync(): Promise<BalancesByOwner> {
+ const balancesByOwner: BalancesByOwner = {};
+ for (const tokenContractInstance of this.tokenContractInstances) {
+ for (const ownerAddress of this.ownerAddresses) {
+ let balance = await tokenContractInstance.balanceOf(ownerAddress);
+ balance = new BigNumber(balance);
+ if (_.isUndefined(balancesByOwner[ownerAddress])) {
+ balancesByOwner[ownerAddress] = {};
+ }
+ balancesByOwner[ownerAddress][tokenContractInstance.address] = balance;
+ }
+ }
+ return balancesByOwner;
+ }
+}
diff --git a/packages/contracts/util/bignumber_config.ts b/packages/contracts/util/bignumber_config.ts
new file mode 100644
index 000000000..38f59d341
--- /dev/null
+++ b/packages/contracts/util/bignumber_config.ts
@@ -0,0 +1,11 @@
+import {BigNumber} from 'bignumber.js';
+
+export const bigNumberConfigs = {
+ configure() {
+ // By default BigNumber's `toString` method converts to exponential notation if the value has
+ // more then 20 digits. We want to avoid this behavior, so we set EXPONENTIAL_AT to a high number
+ BigNumber.config({
+ EXPONENTIAL_AT: 1000,
+ });
+ },
+};
diff --git a/packages/contracts/util/constants.ts b/packages/contracts/util/constants.ts
new file mode 100644
index 000000000..5beebc68c
--- /dev/null
+++ b/packages/contracts/util/constants.ts
@@ -0,0 +1,4 @@
+export const constants = {
+ NULL_BYTES: '0x',
+ INVALID_OPCODE: 'invalid opcode',
+};
diff --git a/packages/contracts/util/crypto.ts b/packages/contracts/util/crypto.ts
new file mode 100644
index 000000000..e4baee021
--- /dev/null
+++ b/packages/contracts/util/crypto.ts
@@ -0,0 +1,38 @@
+import {BigNumber} from 'bignumber.js';
+import BN = require('bn.js');
+import ABI = require('ethereumjs-abi');
+import ethUtil = require('ethereumjs-util');
+import * as _ from 'lodash';
+
+export const crypto = {
+ /*
+ * We convert types from JS to Solidity as follows:
+ * BigNumber -> uint256
+ * number -> uint8
+ * string -> string
+ * boolean -> bool
+ * valid Ethereum address -> address
+ */
+ solSHA3(args: any[]): Buffer {
+ const argTypes: string[] = [];
+ _.each(args, (arg, i) => {
+ const isNumber = _.isFinite(arg);
+ if (isNumber) {
+ argTypes.push('uint8');
+ } else if ((arg).isBigNumber) {
+ argTypes.push('uint256');
+ args[i] = new BN(arg.toString(10), 10);
+ } else if (ethUtil.isValidAddress(arg)) {
+ argTypes.push('address');
+ } else if (_.isString(arg)) {
+ argTypes.push('string');
+ } else if (_.isBoolean(arg)) {
+ argTypes.push('bool');
+ } else {
+ throw new Error(`Unable to guess arg type: ${arg}`);
+ }
+ });
+ const hash = ABI.soliditySHA3(argTypes, args);
+ return hash;
+ },
+};
diff --git a/packages/contracts/util/exchange_wrapper.ts b/packages/contracts/util/exchange_wrapper.ts
new file mode 100644
index 000000000..062ea292b
--- /dev/null
+++ b/packages/contracts/util/exchange_wrapper.ts
@@ -0,0 +1,168 @@
+import {BigNumber} from 'bignumber.js';
+import * as _ from 'lodash';
+
+import {formatters} from './formatters';
+import {Order} from './order';
+import {ContractInstance} from './types';
+
+export class ExchangeWrapper {
+ private exchange: ContractInstance;
+ constructor(exchangeContractInstance: ContractInstance) {
+ this.exchange = exchangeContractInstance;
+ }
+ public async fillOrderAsync(order: Order, from: string,
+ opts: {
+ fillTakerTokenAmount?: BigNumber;
+ shouldThrowOnInsufficientBalanceOrAllowance?: boolean;
+ } = {}) {
+ const shouldThrowOnInsufficientBalanceOrAllowance = !!opts.shouldThrowOnInsufficientBalanceOrAllowance;
+ const params = order.createFill(shouldThrowOnInsufficientBalanceOrAllowance, opts.fillTakerTokenAmount);
+ const tx = await this.exchange.fillOrder(
+ params.orderAddresses,
+ params.orderValues,
+ params.fillTakerTokenAmount,
+ params.shouldThrowOnInsufficientBalanceOrAllowance,
+ params.v,
+ params.r,
+ params.s,
+ {from},
+ );
+ _.each(tx.logs, log => wrapLogBigNumbers(log));
+ return tx;
+ }
+ public async cancelOrderAsync(order: Order, from: string,
+ opts: {cancelTakerTokenAmount?: BigNumber} = {}) {
+ const params = order.createCancel(opts.cancelTakerTokenAmount);
+ const tx = await this.exchange.cancelOrder(
+ params.orderAddresses,
+ params.orderValues,
+ params.cancelTakerTokenAmount,
+ {from},
+ );
+ _.each(tx.logs, log => wrapLogBigNumbers(log));
+ return tx;
+ }
+ public async fillOrKillOrderAsync(order: Order, from: string,
+ opts: {fillTakerTokenAmount?: BigNumber} = {}) {
+ const shouldThrowOnInsufficientBalanceOrAllowance = true;
+ const params = order.createFill(shouldThrowOnInsufficientBalanceOrAllowance, opts.fillTakerTokenAmount);
+ const tx = await this.exchange.fillOrKillOrder(
+ params.orderAddresses,
+ params.orderValues,
+ params.fillTakerTokenAmount,
+ params.v,
+ params.r,
+ params.s,
+ {from},
+ );
+ _.each(tx.logs, log => wrapLogBigNumbers(log));
+ return tx;
+ }
+ public async batchFillOrdersAsync(orders: Order[], from: string,
+ opts: {
+ fillTakerTokenAmounts?: BigNumber[];
+ shouldThrowOnInsufficientBalanceOrAllowance?: boolean;
+ } = {}) {
+ const shouldThrowOnInsufficientBalanceOrAllowance = !!opts.shouldThrowOnInsufficientBalanceOrAllowance;
+ const params = formatters.createBatchFill(
+ orders, shouldThrowOnInsufficientBalanceOrAllowance, opts.fillTakerTokenAmounts);
+ const tx = await this.exchange.batchFillOrders(
+ params.orderAddresses,
+ params.orderValues,
+ params.fillTakerTokenAmounts,
+ params.shouldThrowOnInsufficientBalanceOrAllowance,
+ params.v,
+ params.r,
+ params.s,
+ {from},
+ );
+ _.each(tx.logs, log => wrapLogBigNumbers(log));
+ return tx;
+ }
+ public async batchFillOrKillOrdersAsync(orders: Order[], from: string,
+ opts: {
+ fillTakerTokenAmounts?: BigNumber[];
+ } = {}) {
+ const params = formatters.createBatchFill(orders, undefined, opts.fillTakerTokenAmounts);
+ const tx = await this.exchange.batchFillOrKillOrders(
+ params.orderAddresses,
+ params.orderValues,
+ params.fillTakerTokenAmounts,
+ params.v,
+ params.r,
+ params.s,
+ {from},
+ );
+ _.each(tx.logs, log => wrapLogBigNumbers(log));
+ return tx;
+ }
+ public async fillOrdersUpToAsync(orders: Order[], from: string,
+ opts: {fillTakerTokenAmount?: BigNumber;
+ shouldThrowOnInsufficientBalanceOrAllowance?: boolean;
+ } = {}) {
+ const shouldThrowOnInsufficientBalanceOrAllowance = !!opts.shouldThrowOnInsufficientBalanceOrAllowance;
+ const params = formatters.createFillUpTo(orders,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ opts.fillTakerTokenAmount);
+ const tx = await this.exchange.fillOrdersUpTo(
+ params.orderAddresses,
+ params.orderValues,
+ params.fillTakerTokenAmount,
+ params.shouldThrowOnInsufficientBalanceOrAllowance,
+ params.v,
+ params.r,
+ params.s,
+ {from},
+ );
+ _.each(tx.logs, log => wrapLogBigNumbers(log));
+ return tx;
+ }
+ public async batchCancelOrdersAsync(orders: Order[], from: string,
+ opts: {cancelTakerTokenAmounts?: BigNumber[]} = {}) {
+ const params = formatters.createBatchCancel(orders, opts.cancelTakerTokenAmounts);
+ const tx = await this.exchange.batchCancelOrders(
+ params.orderAddresses,
+ params.orderValues,
+ params.cancelTakerTokenAmounts,
+ {from},
+ );
+ _.each(tx.logs, log => wrapLogBigNumbers(log));
+ return tx;
+ }
+ public async getOrderHashAsync(order: Order): Promise<string> {
+ const shouldThrowOnInsufficientBalanceOrAllowance = false;
+ const params = order.createFill(shouldThrowOnInsufficientBalanceOrAllowance);
+ const orderHash = await this.exchange.getOrderHash(params.orderAddresses, params.orderValues);
+ return orderHash;
+ }
+ public async isValidSignatureAsync(order: Order): Promise<boolean> {
+ const isValidSignature = await this.exchange.isValidSignature(
+ order.params.maker,
+ order.params.orderHashHex,
+ order.params.v,
+ order.params.r,
+ order.params.s,
+ );
+ return isValidSignature;
+ }
+ public async isRoundingErrorAsync(numerator: BigNumber, denominator: BigNumber,
+ target: BigNumber): Promise<boolean> {
+ const isRoundingError = await this.exchange.isRoundingError(numerator, denominator, target);
+ return isRoundingError;
+ }
+ public async getPartialAmountAsync(numerator: BigNumber, denominator: BigNumber,
+ target: BigNumber): Promise<BigNumber> {
+ const partialAmount = new BigNumber(await this.exchange.getPartialAmount(numerator, denominator, target));
+ return partialAmount;
+ }
+}
+
+function wrapLogBigNumbers(log: any): any {
+ const argNames = _.keys(log.args);
+ for (const argName of argNames) {
+ const isWeb3BigNumber = _.startsWith(log.args[argName].constructor.toString(), 'function BigNumber(');
+ if (isWeb3BigNumber) {
+ log.args[argName] = new BigNumber(log.args[argName]);
+ }
+ }
+}
diff --git a/packages/contracts/util/formatters.ts b/packages/contracts/util/formatters.ts
new file mode 100644
index 000000000..cda548617
--- /dev/null
+++ b/packages/contracts/util/formatters.ts
@@ -0,0 +1,74 @@
+import {BigNumber} from 'bignumber.js';
+import * as _ from 'lodash';
+
+import {Order} from './order';
+import {BatchCancelOrders, BatchFillOrders, FillOrdersUpTo} from './types';
+
+export const formatters = {
+ createBatchFill(orders: Order[],
+ shouldThrowOnInsufficientBalanceOrAllowance: boolean,
+ fillTakerTokenAmounts: BigNumber[] = []) {
+ const batchFill: BatchFillOrders = {
+ orderAddresses: [],
+ orderValues: [],
+ fillTakerTokenAmounts,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ v: [],
+ r: [],
+ s: [],
+ };
+ _.forEach(orders, order => {
+ batchFill.orderAddresses.push([order.params.maker, order.params.taker, order.params.makerToken,
+ order.params.takerToken, order.params.feeRecipient]);
+ batchFill.orderValues.push([order.params.makerTokenAmount, order.params.takerTokenAmount, order.params.makerFee,
+ order.params.takerFee, order.params.expirationTimestampInSec, order.params.salt]);
+ batchFill.v.push(order.params.v);
+ batchFill.r.push(order.params.r);
+ batchFill.s.push(order.params.s);
+ if (fillTakerTokenAmounts.length < orders.length) {
+ batchFill.fillTakerTokenAmounts.push(order.params.takerTokenAmount);
+ }
+ });
+ return batchFill;
+ },
+ createFillUpTo(orders: Order[],
+ shouldThrowOnInsufficientBalanceOrAllowance: boolean,
+ fillTakerTokenAmount: BigNumber) {
+ const fillUpTo: FillOrdersUpTo = {
+ orderAddresses: [],
+ orderValues: [],
+ fillTakerTokenAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance,
+ v: [],
+ r: [],
+ s: [],
+ };
+ orders.forEach(order => {
+ fillUpTo.orderAddresses.push([order.params.maker, order.params.taker, order.params.makerToken,
+ order.params.takerToken, order.params.feeRecipient]);
+ fillUpTo.orderValues.push([order.params.makerTokenAmount, order.params.takerTokenAmount, order.params.makerFee,
+ order.params.takerFee, order.params.expirationTimestampInSec, order.params.salt]);
+ fillUpTo.v.push(order.params.v);
+ fillUpTo.r.push(order.params.r);
+ fillUpTo.s.push(order.params.s);
+ });
+ return fillUpTo;
+ },
+ createBatchCancel(orders: Order[], cancelTakerTokenAmounts: BigNumber[] = []) {
+ const batchCancel: BatchCancelOrders = {
+ orderAddresses: [],
+ orderValues: [],
+ cancelTakerTokenAmounts,
+ };
+ orders.forEach(order => {
+ batchCancel.orderAddresses.push([order.params.maker, order.params.taker, order.params.makerToken,
+ order.params.takerToken, order.params.feeRecipient]);
+ batchCancel.orderValues.push([order.params.makerTokenAmount, order.params.takerTokenAmount, order.params.makerFee,
+ order.params.takerFee, order.params.expirationTimestampInSec, order.params.salt]);
+ if (cancelTakerTokenAmounts.length < orders.length) {
+ batchCancel.cancelTakerTokenAmounts.push(order.params.takerTokenAmount);
+ }
+ });
+ return batchCancel;
+ },
+};
diff --git a/packages/contracts/util/multi_sig_wrapper.ts b/packages/contracts/util/multi_sig_wrapper.ts
new file mode 100644
index 000000000..7c13dae97
--- /dev/null
+++ b/packages/contracts/util/multi_sig_wrapper.ts
@@ -0,0 +1,34 @@
+import ABI = require('ethereumjs-abi');
+import ethUtil = require('ethereumjs-util');
+import * as _ from 'lodash';
+import * as Web3 from 'web3';
+
+import {ContractInstance, TransactionDataParams} from './types';
+
+export class MultiSigWrapper {
+ private multiSig: ContractInstance;
+ constructor(multiSigContractInstance: ContractInstance) {
+ this.multiSig = multiSigContractInstance;
+ }
+ public async submitTransactionAsync(destination: string, from: string,
+ dataParams: TransactionDataParams,
+ value: number = 0) {
+ const {name, abi, args = []} = dataParams;
+ const encoded = this.encodeFnArgs(name, abi, args);
+ return this.multiSig.submitTransaction(destination, value, encoded, {from});
+ }
+ public encodeFnArgs(name: string, abi: Web3.AbiDefinition[], args: any[]) {
+ const abiEntity = _.find(abi, {name}) as Web3.MethodAbi;
+ if (_.isUndefined(abiEntity)) {
+ throw new Error(`Did not find abi entry for name: ${name}`);
+ }
+ const types = _.map(abiEntity.inputs, input => input.type);
+ const funcSig = ethUtil.bufferToHex(ABI.methodID(name, types));
+ const argsData = _.map(args, arg => {
+ const target = _.isBoolean(arg) ? +arg : arg;
+ const targetBuff = ethUtil.toBuffer(target);
+ return ethUtil.setLengthLeft(targetBuff, 32).toString('hex');
+ });
+ return funcSig + argsData.join('');
+ }
+}
diff --git a/packages/contracts/util/order.ts b/packages/contracts/util/order.ts
new file mode 100644
index 000000000..635999348
--- /dev/null
+++ b/packages/contracts/util/order.ts
@@ -0,0 +1,109 @@
+import {BigNumber} from 'bignumber.js';
+import promisify = require('es6-promisify');
+import ethUtil = require('ethereumjs-util');
+import * as _ from 'lodash';
+import Web3 = require('web3');
+
+import {crypto} from './crypto';
+import {OrderParams} from './types';
+
+// In order to benefit from type-safety, we re-assign the global web3 instance injected by Truffle
+// with type `any` to a variable of type `Web3`.
+const web3: Web3 = (global as any).web3;
+
+export class Order {
+ public params: OrderParams;
+ constructor(params: OrderParams) {
+ this.params = params;
+ }
+ public isValidSignature() {
+ const {v, r, s} = this.params;
+ if (_.isUndefined(v) || _.isUndefined(r) || _.isUndefined(s)) {
+ throw new Error('Cannot call isValidSignature on unsigned order');
+ }
+ const orderHash = this.getOrderHash();
+ const msgHash = ethUtil.hashPersonalMessage(ethUtil.toBuffer(orderHash));
+ try {
+ const pubKey = ethUtil.ecrecover(msgHash, v, ethUtil.toBuffer(r), ethUtil.toBuffer(s));
+ const recoveredAddress = ethUtil.bufferToHex(ethUtil.pubToAddress(pubKey));
+ return recoveredAddress === this.params.maker;
+ } catch (err) {
+ return false;
+ }
+ }
+ public async signAsync() {
+ const orderHash = this.getOrderHash();
+ const signature = await promisify(web3.eth.sign)(this.params.maker, orderHash);
+ const {v, r, s} = ethUtil.fromRpcSig(signature);
+ this.params = _.assign(this.params, {
+ orderHashHex: orderHash,
+ v,
+ r: ethUtil.bufferToHex(r),
+ s: ethUtil.bufferToHex(s),
+ });
+ }
+ public createFill(shouldThrowOnInsufficientBalanceOrAllowance?: boolean, fillTakerTokenAmount?: BigNumber) {
+ const fill = {
+ orderAddresses: [
+ this.params.maker,
+ this.params.taker,
+ this.params.makerToken,
+ this.params.takerToken,
+ this.params.feeRecipient,
+ ],
+ orderValues: [
+ this.params.makerTokenAmount,
+ this.params.takerTokenAmount,
+ this.params.makerFee,
+ this.params.takerFee,
+ this.params.expirationTimestampInSec,
+ this.params.salt,
+ ],
+ fillTakerTokenAmount: fillTakerTokenAmount || this.params.takerTokenAmount,
+ shouldThrowOnInsufficientBalanceOrAllowance: !!shouldThrowOnInsufficientBalanceOrAllowance,
+ v: this.params.v,
+ r: this.params.r,
+ s: this.params.s,
+ };
+ return fill;
+ }
+ public createCancel(cancelTakerTokenAmount?: BigNumber) {
+ const cancel = {
+ orderAddresses: [
+ this.params.maker,
+ this.params.taker,
+ this.params.makerToken,
+ this.params.takerToken,
+ this.params.feeRecipient,
+ ],
+ orderValues: [
+ this.params.makerTokenAmount,
+ this.params.takerTokenAmount,
+ this.params.makerFee,
+ this.params.takerFee,
+ this.params.expirationTimestampInSec,
+ this.params.salt,
+ ],
+ cancelTakerTokenAmount: cancelTakerTokenAmount || this.params.takerTokenAmount,
+ };
+ return cancel;
+ }
+ private getOrderHash(): string {
+ const orderHash = crypto.solSHA3([
+ this.params.exchangeContractAddress,
+ this.params.maker,
+ this.params.taker,
+ this.params.makerToken,
+ this.params.takerToken,
+ this.params.feeRecipient,
+ this.params.makerTokenAmount,
+ this.params.takerTokenAmount,
+ this.params.makerFee,
+ this.params.takerFee,
+ this.params.expirationTimestampInSec,
+ this.params.salt,
+ ]);
+ const orderHashHex = ethUtil.bufferToHex(orderHash);
+ return orderHashHex;
+ }
+}
diff --git a/packages/contracts/util/order_factory.ts b/packages/contracts/util/order_factory.ts
new file mode 100644
index 000000000..cdce7d18a
--- /dev/null
+++ b/packages/contracts/util/order_factory.ts
@@ -0,0 +1,24 @@
+import {ZeroEx} from '0x.js';
+import {BigNumber} from 'bignumber.js';
+import * as _ from 'lodash';
+
+import {Order} from './order';
+import {DefaultOrderParams, OptionalOrderParams, OrderParams} from './types';
+
+export class OrderFactory {
+ private defaultOrderParams: DefaultOrderParams;
+ constructor(defaultOrderParams: DefaultOrderParams) {
+ this.defaultOrderParams = defaultOrderParams;
+ }
+ public async newSignedOrderAsync(customOrderParams: OptionalOrderParams = {}) {
+ const randomExpiration = new BigNumber(Math.floor((Date.now() + (Math.random() * 100000000000)) / 1000));
+ const orderParams: OrderParams = _.assign({}, {
+ expirationTimestampInSec: randomExpiration,
+ salt: ZeroEx.generatePseudoRandomSalt(),
+ taker: ZeroEx.NULL_ADDRESS,
+ }, this.defaultOrderParams, customOrderParams);
+ const order = new Order(orderParams);
+ await order.signAsync();
+ return order;
+ }
+}
diff --git a/packages/contracts/util/rpc.ts b/packages/contracts/util/rpc.ts
new file mode 100644
index 000000000..37643874c
--- /dev/null
+++ b/packages/contracts/util/rpc.ts
@@ -0,0 +1,43 @@
+import 'isomorphic-fetch';
+
+import * as truffleConf from '../truffle.js';
+
+export class RPC {
+ private host: string;
+ private port: number;
+ private id: number;
+ constructor() {
+ this.host = truffleConf.networks.development.host;
+ this.port = truffleConf.networks.development.port;
+ this.id = 0;
+ }
+ public async increaseTimeAsync(time: number) {
+ const method = 'evm_increaseTime';
+ const params = [time];
+ const payload = this.toPayload(method, params);
+ return this.sendAsync(payload);
+ }
+ public async mineBlockAsync() {
+ const method = 'evm_mine';
+ const payload = this.toPayload(method);
+ return this.sendAsync(payload);
+ }
+ private toPayload(method: string, params: any[] = []) {
+ const payload = JSON.stringify({
+ id: this.id,
+ method,
+ params,
+ });
+ this.id++;
+ return payload;
+ }
+ private async sendAsync(payload: string): Promise<any> {
+ const opts = {
+ method: 'POST',
+ body: payload,
+ };
+ const response = await fetch(`http://${this.host}:${this.port}`, opts);
+ const responsePayload = await response.json();
+ return responsePayload;
+ }
+}
diff --git a/packages/contracts/util/token_registry_wrapper.ts b/packages/contracts/util/token_registry_wrapper.ts
new file mode 100644
index 000000000..5065a1802
--- /dev/null
+++ b/packages/contracts/util/token_registry_wrapper.ts
@@ -0,0 +1,56 @@
+import {ContractInstance, Token} from './types';
+
+export class TokenRegWrapper {
+ private tokenReg: ContractInstance;
+ constructor(tokenRegContractInstance: ContractInstance) {
+ this.tokenReg = tokenRegContractInstance;
+ }
+ public addTokenAsync(token: Token, from: string) {
+ const tx = this.tokenReg.addToken(
+ token.address,
+ token.name,
+ token.symbol,
+ token.decimals,
+ token.ipfsHash,
+ token.swarmHash,
+ {from},
+ );
+ return tx;
+ }
+ public async getTokenMetaDataAsync(tokenAddress: string) {
+ const data = await this.tokenReg.getTokenMetaData(tokenAddress);
+ const token: Token = {
+ address: data[0],
+ name: data[1],
+ symbol: data[2],
+ decimals: data[3].toNumber(),
+ ipfsHash: data[4],
+ swarmHash: data[5],
+ };
+ return token;
+ }
+ public async getTokenByNameAsync(tokenName: string) {
+ const data = await this.tokenReg.getTokenByName(tokenName);
+ const token: Token = {
+ address: data[0],
+ name: data[1],
+ symbol: data[2],
+ decimals: data[3].toNumber(),
+ ipfsHash: data[4],
+ swarmHash: data[5],
+ };
+ return token;
+ }
+ public async getTokenBySymbolAsync(tokenSymbol: string) {
+ const data = await this.tokenReg.getTokenBySymbol(tokenSymbol);
+ const token: Token = {
+ address: data[0],
+ name: data[1],
+ symbol: data[2],
+ decimals: data[3].toNumber(),
+ ipfsHash: data[4],
+ swarmHash: data[5],
+ };
+ return token;
+ }
+}
diff --git a/packages/contracts/util/types.ts b/packages/contracts/util/types.ts
new file mode 100644
index 000000000..2e16fac7a
--- /dev/null
+++ b/packages/contracts/util/types.ts
@@ -0,0 +1,119 @@
+import {BigNumber} from 'bignumber.js';
+import * as Web3 from 'web3';
+
+export interface BalancesByOwner {
+ [ownerAddress: string]: {
+ [tokenAddress: string]: BigNumber;
+ };
+}
+
+export interface BatchFillOrders {
+ orderAddresses: string[][];
+ orderValues: BigNumber[][];
+ fillTakerTokenAmounts: BigNumber[];
+ shouldThrowOnInsufficientBalanceOrAllowance: boolean;
+ v: number[];
+ r: string[];
+ s: string[];
+}
+
+export interface FillOrdersUpTo {
+ orderAddresses: string[][];
+ orderValues: BigNumber[][];
+ fillTakerTokenAmount: BigNumber;
+ shouldThrowOnInsufficientBalanceOrAllowance: boolean;
+ v: number[];
+ r: string[];
+ s: string[];
+}
+
+export interface BatchCancelOrders {
+ orderAddresses: string[][];
+ orderValues: BigNumber[][];
+ cancelTakerTokenAmounts: BigNumber[];
+}
+
+export interface DefaultOrderParams {
+ exchangeContractAddress: string;
+ maker: string;
+ feeRecipient: string;
+ makerToken: string;
+ takerToken: string;
+ makerTokenAmount: BigNumber;
+ takerTokenAmount: BigNumber;
+ makerFee: BigNumber;
+ takerFee: BigNumber;
+}
+
+export interface OptionalOrderParams {
+ exchangeContractAddress?: string;
+ maker?: string;
+ taker?: string;
+ feeRecipient?: string;
+ makerToken?: string;
+ takerToken?: string;
+ makerTokenAmount?: BigNumber;
+ takerTokenAmount?: BigNumber;
+ makerFee?: BigNumber;
+ takerFee?: BigNumber;
+ expirationTimestampInSec?: BigNumber;
+}
+
+export interface OrderParams {
+ exchangeContractAddress: string;
+ maker: string;
+ taker: string;
+ feeRecipient: string;
+ makerToken: string;
+ takerToken: string;
+ makerTokenAmount: BigNumber;
+ takerTokenAmount: BigNumber;
+ makerFee: BigNumber;
+ takerFee: BigNumber;
+ expirationTimestampInSec: BigNumber;
+ salt: BigNumber;
+ orderHashHex?: string;
+ v?: number;
+ r?: string;
+ s?: string;
+}
+
+export interface TransactionDataParams {
+ name: string;
+ abi: Web3.AbiDefinition[];
+ args: any[];
+}
+
+export interface Token {
+ address?: string;
+ name: string;
+ symbol: string;
+ decimals: number;
+ ipfsHash: string;
+ swarmHash: string;
+}
+
+export interface MultiSigConfig {
+ owners: string[];
+ confirmationsRequired: number;
+ secondsRequired: number;
+}
+
+export interface MultiSigConfigByNetwork {
+ [networkName: string]: MultiSigConfig;
+}
+
+export interface TokenInfoByNetwork {
+ development: Token[];
+ live: Token[];
+}
+
+// Named type aliases to improve readability
+export type ContractInstance = any;
+
+export enum ExchangeContractErrs {
+ ERROR_ORDER_EXPIRED,
+ ERROR_ORDER_FULLY_FILLED_OR_CANCELLED,
+ ERROR_ROUNDING_ERROR_TOO_LARGE,
+ ERROR_INSUFFICIENT_BALANCE_OR_ALLOWANCE,
+}