33
src/0x.js.ts
33
src/0x.js.ts
@@ -4,22 +4,20 @@ import {bigNumberConfigs} from './bignumber_config';
|
||||
import * as ethUtil from 'ethereumjs-util';
|
||||
import contract = require('truffle-contract');
|
||||
import * as Web3 from 'web3';
|
||||
import * as ethABI from 'ethereumjs-abi';
|
||||
import findVersions = require('find-versions');
|
||||
import compareVersions = require('compare-versions');
|
||||
import {Web3Wrapper} from './web3_wrapper';
|
||||
import {constants} from './utils/constants';
|
||||
import {utils} from './utils/utils';
|
||||
import {assert} from './utils/assert';
|
||||
import {SchemaValidator} from './utils/schema_validator';
|
||||
import {ExchangeWrapper} from './contract_wrappers/exchange_wrapper';
|
||||
import {TokenRegistryWrapper} from './contract_wrappers/token_registry_wrapper';
|
||||
import {ecSignatureSchema} from './schemas/ec_signature_schema';
|
||||
import {TokenWrapper} from './contract_wrappers/token_wrapper';
|
||||
import {SolidityTypes, ECSignature, ZeroExError} from './types';
|
||||
import {Order, SignedOrder} from './types';
|
||||
import {orderSchema} from './schemas/order_schemas';
|
||||
import {ECSignature, ZeroExError, Order, SignedOrder} from './types';
|
||||
import * as ExchangeArtifacts from './artifacts/Exchange.json';
|
||||
import {SchemaValidator} from './utils/schema_validator';
|
||||
import {orderSchema} from './schemas/order_schemas';
|
||||
|
||||
// Customize our BigNumber instances
|
||||
bigNumberConfigs.configure();
|
||||
@@ -132,29 +130,10 @@ export class ZeroEx {
|
||||
* Computes the orderHash for a given order and returns it as a hex encoded string.
|
||||
*/
|
||||
public async getOrderHashHexAsync(order: Order|SignedOrder): Promise<string> {
|
||||
assert.doesConformToSchema(
|
||||
'order', SchemaValidator.convertToJSONSchemaCompatibleObject(order as object), orderSchema);
|
||||
const exchangeContractAddr = await this.getExchangeAddressAsync();
|
||||
assert.doesConformToSchema('order',
|
||||
SchemaValidator.convertToJSONSchemaCompatibleObject(order as object),
|
||||
orderSchema);
|
||||
|
||||
const orderParts = [
|
||||
{value: exchangeContractAddr, 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: utils.bigNumberToBN(order.makerTokenAmount), type: SolidityTypes.uint256},
|
||||
{value: utils.bigNumberToBN(order.takerTokenAmount), type: SolidityTypes.uint256},
|
||||
{value: utils.bigNumberToBN(order.makerFee), type: SolidityTypes.uint256},
|
||||
{value: utils.bigNumberToBN(order.takerFee), type: SolidityTypes.uint256},
|
||||
{value: utils.bigNumberToBN(order.expirationUnixTimestampSec), type: SolidityTypes.uint256},
|
||||
{value: utils.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);
|
||||
const hashHex = utils.getOrderHashHex(order, exchangeContractAddr);
|
||||
return hashHex;
|
||||
}
|
||||
/**
|
||||
|
@@ -9,9 +9,9 @@ import {
|
||||
ExchangeContractErrs,
|
||||
OrderValues,
|
||||
OrderAddresses,
|
||||
Order,
|
||||
SignedOrder,
|
||||
ContractEvent,
|
||||
ZeroExError,
|
||||
ExchangeEvents,
|
||||
SubscriptionOpts,
|
||||
IndexFilterValues,
|
||||
@@ -25,7 +25,7 @@ import {utils} from '../utils/utils';
|
||||
import {ContractWrapper} from './contract_wrapper';
|
||||
import * as ExchangeArtifacts from '../artifacts/Exchange.json';
|
||||
import {ecSignatureSchema} from '../schemas/ec_signature_schema';
|
||||
import {signedOrderSchema} from '../schemas/order_schemas';
|
||||
import {signedOrderSchema, orderSchema} from '../schemas/order_schemas';
|
||||
import {SchemaValidator} from '../utils/schema_validator';
|
||||
import {constants} from '../utils/constants';
|
||||
import {TokenWrapper} from './token_wrapper';
|
||||
@@ -42,6 +42,24 @@ export class ExchangeWrapper extends ContractWrapper {
|
||||
private exchangeContractIfExists?: ExchangeContract;
|
||||
private exchangeLogEventObjs: ContractEventObj[];
|
||||
private tokenWrapper: TokenWrapper;
|
||||
private static 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];
|
||||
}
|
||||
constructor(web3Wrapper: Web3Wrapper, tokenWrapper: TokenWrapper) {
|
||||
super(web3Wrapper);
|
||||
this.tokenWrapper = tokenWrapper;
|
||||
@@ -126,21 +144,7 @@ export class ExchangeWrapper extends ContractWrapper {
|
||||
const exchangeInstance = await this.getExchangeContractAsync();
|
||||
await this.validateFillOrderAndThrowIfInvalidAsync(signedOrder, fillTakerAmount, takerAddress);
|
||||
|
||||
const orderAddresses: OrderAddresses = [
|
||||
signedOrder.maker,
|
||||
signedOrder.taker,
|
||||
signedOrder.makerTokenAddress,
|
||||
signedOrder.takerTokenAddress,
|
||||
signedOrder.feeRecipient,
|
||||
];
|
||||
const orderValues: OrderValues = [
|
||||
signedOrder.makerTokenAmount,
|
||||
signedOrder.takerTokenAmount,
|
||||
signedOrder.makerFee,
|
||||
signedOrder.takerFee,
|
||||
signedOrder.expirationUnixTimestampSec,
|
||||
signedOrder.salt,
|
||||
];
|
||||
const [orderAddresses, orderValues] = ExchangeWrapper.getOrderAddressesAndValues(signedOrder);
|
||||
const gas = await exchangeInstance.fill.estimateGas(
|
||||
orderAddresses,
|
||||
orderValues,
|
||||
@@ -168,6 +172,39 @@ export class ExchangeWrapper extends ContractWrapper {
|
||||
);
|
||||
this.throwErrorLogsAsErrors(response.logs);
|
||||
}
|
||||
/**
|
||||
* Cancel a given fill amount of an order. Cancellations are cumulative.
|
||||
*/
|
||||
public async cancelOrderAsync(order: Order|SignedOrder, takerTokenCancelAmount: BigNumber.BigNumber): Promise<void> {
|
||||
assert.doesConformToSchema('order',
|
||||
SchemaValidator.convertToJSONSchemaCompatibleObject(order as object),
|
||||
orderSchema);
|
||||
assert.isBigNumber('takerTokenCancelAmount', takerTokenCancelAmount);
|
||||
await assert.isSenderAddressAvailableAsync(this.web3Wrapper, 'order.maker', order.maker);
|
||||
|
||||
const exchangeInstance = await this.getExchangeContractAsync();
|
||||
await this.validateCancelOrderAndThrowIfInvalidAsync(order, takerTokenCancelAmount);
|
||||
|
||||
const [orderAddresses, orderValues] = ExchangeWrapper.getOrderAddressesAndValues(order);
|
||||
const gas = await exchangeInstance.cancel.estimateGas(
|
||||
orderAddresses,
|
||||
orderValues,
|
||||
takerTokenCancelAmount,
|
||||
{
|
||||
from: order.maker,
|
||||
},
|
||||
);
|
||||
const response: ContractResponse = await exchangeInstance.cancel(
|
||||
orderAddresses,
|
||||
orderValues,
|
||||
takerTokenCancelAmount,
|
||||
{
|
||||
from: order.maker,
|
||||
gas,
|
||||
},
|
||||
);
|
||||
this.throwErrorLogsAsErrors(response.logs);
|
||||
}
|
||||
/**
|
||||
* Subscribe to an event type emitted by the Exchange smart contract
|
||||
*/
|
||||
@@ -194,6 +231,12 @@ export class ExchangeWrapper extends ContractWrapper {
|
||||
logEventObj.watch(callback);
|
||||
this.exchangeLogEventObjs.push(logEventObj);
|
||||
}
|
||||
private async getOrderHashAsync(order: Order|SignedOrder): Promise<string> {
|
||||
const [orderAddresses, orderValues] = ExchangeWrapper.getOrderAddressesAndValues(order);
|
||||
const exchangeInstance = await this.getExchangeContractAsync();
|
||||
const orderHash = utils.getOrderHashHex(order, exchangeInstance.address);
|
||||
return orderHash;
|
||||
}
|
||||
private async stopWatchingExchangeLogEventsAsync() {
|
||||
const stopWatchingPromises = _.map(this.exchangeLogEventObjs, logEventObj => {
|
||||
return promisify(logEventObj.stopWatching, logEventObj)();
|
||||
@@ -210,7 +253,7 @@ export class ExchangeWrapper extends ContractWrapper {
|
||||
if (signedOrder.taker !== constants.NULL_ADDRESS && signedOrder.taker !== senderAddress) {
|
||||
throw new Error(ExchangeContractErrs.TRANSACTION_SENDER_IS_NOT_FILL_ORDER_TAKER);
|
||||
}
|
||||
const currentUnixTimestampSec = Date.now() / 1000;
|
||||
const currentUnixTimestampSec = utils.getCurrentUnixTimestamp();
|
||||
if (signedOrder.expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) {
|
||||
throw new Error(ExchangeContractErrs.ORDER_FILL_EXPIRED);
|
||||
}
|
||||
@@ -225,7 +268,21 @@ export class ExchangeWrapper extends ContractWrapper {
|
||||
throw new Error(ExchangeContractErrs.ORDER_FILL_ROUNDING_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
private async validateCancelOrderAndThrowIfInvalidAsync(
|
||||
order: Order, takerTokenCancelAmount: BigNumber.BigNumber): Promise<void> {
|
||||
if (takerTokenCancelAmount.eq(0)) {
|
||||
throw new Error(ExchangeContractErrs.ORDER_CANCEL_AMOUNT_ZERO);
|
||||
}
|
||||
const orderHash = await this.getOrderHashAsync(order);
|
||||
const unavailableAmount = await this.getUnavailableTakerAmountAsync(orderHash);
|
||||
if (order.takerTokenAmount.minus(unavailableAmount).eq(0)) {
|
||||
throw new Error(ExchangeContractErrs.ORDER_ALREADY_CANCELLED_OR_FILLED);
|
||||
}
|
||||
const currentUnixTimestampSec = utils.getCurrentUnixTimestamp();
|
||||
if (order.expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) {
|
||||
throw new Error(ExchangeContractErrs.ORDER_CANCEL_EXPIRED);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This method does not currently validate the edge-case where the makerToken or takerToken is also the token used
|
||||
* to pay fees (ZRX). It is possible for them to have enough for fees and the transfer but not both.
|
||||
|
4
src/globals.d.ts
vendored
4
src/globals.d.ts
vendored
@@ -47,7 +47,9 @@ declare module 'ethereumjs-util' {
|
||||
}
|
||||
|
||||
// truffle-contract declarations
|
||||
declare interface ContractInstance {}
|
||||
declare interface ContractInstance {
|
||||
address: string;
|
||||
}
|
||||
declare interface ContractFactory {
|
||||
setProvider: (providerObj: any) => void;
|
||||
deployed: () => ContractInstance;
|
||||
|
28
src/types.ts
28
src/types.ts
@@ -44,7 +44,7 @@ export interface ContractEventObj {
|
||||
}
|
||||
export type CreateContractEvent = (indexFilterValues: IndexFilterValues,
|
||||
subscriptionOpts: SubscriptionOpts) => ContractEventObj;
|
||||
export interface ExchangeContract {
|
||||
export interface ExchangeContract extends ContractInstance {
|
||||
isValidSignature: {
|
||||
call: (signerAddressHex: string, dataHex: string, v: number, r: string, s: string,
|
||||
txOpts?: TxOpts) => Promise<boolean>;
|
||||
@@ -64,9 +64,15 @@ export interface ExchangeContract {
|
||||
};
|
||||
fill: {
|
||||
(orderAddresses: OrderAddresses, orderValues: OrderValues, fillAmount: BigNumber.BigNumber,
|
||||
shouldCheckTransfer: boolean, v: number, r: string, s: string, txOpts: TxOpts): ContractResponse;
|
||||
shouldCheckTransfer: boolean, v: number, r: string, s: string, txOpts?: TxOpts): ContractResponse;
|
||||
estimateGas: (orderAddresses: OrderAddresses, orderValues: OrderValues, fillAmount: BigNumber.BigNumber,
|
||||
shouldCheckTransfer: boolean, v: number, r: string, s: string, txOpts: TxOpts) => number;
|
||||
shouldCheckTransfer: boolean, v: number, r: string, s: string, txOpts?: TxOpts) => number;
|
||||
};
|
||||
cancel: {
|
||||
(orderAddresses: OrderAddresses, orderValues: OrderValues, cancelAmount: BigNumber.BigNumber,
|
||||
txOpts?: TxOpts): ContractResponse;
|
||||
estimateGas: (orderAddresses: OrderAddresses, orderValues: OrderValues, cancelAmount: BigNumber.BigNumber,
|
||||
txOpts?: TxOpts) => number;
|
||||
};
|
||||
filled: {
|
||||
call: (orderHash: string) => BigNumber.BigNumber;
|
||||
@@ -74,22 +80,25 @@ export interface ExchangeContract {
|
||||
cancelled: {
|
||||
call: (orderHash: string) => BigNumber.BigNumber;
|
||||
};
|
||||
getOrderHash: {
|
||||
call: (orderAddresses: OrderAddresses, orderValues: OrderValues) => string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface TokenContract {
|
||||
export interface TokenContract extends ContractInstance {
|
||||
balanceOf: {
|
||||
call: (address: string) => Promise<BigNumber.BigNumber>;
|
||||
};
|
||||
allowance: {
|
||||
call: (ownerAddress: string, allowedAddress: string) => Promise<BigNumber.BigNumber>;
|
||||
};
|
||||
transfer: (toAddress: string, amountInBaseUnits: BigNumber.BigNumber, txOpts: TxOpts) => Promise<boolean>;
|
||||
transfer: (toAddress: string, amountInBaseUnits: BigNumber.BigNumber, txOpts?: TxOpts) => Promise<boolean>;
|
||||
transferFrom: (fromAddress: string, toAddress: string, amountInBaseUnits: BigNumber.BigNumber,
|
||||
txOpts: TxOpts) => Promise<boolean>;
|
||||
approve: (proxyAddress: string, amountInBaseUnits: BigNumber.BigNumber, txOpts: TxOpts) => void;
|
||||
txOpts?: TxOpts) => Promise<boolean>;
|
||||
approve: (proxyAddress: string, amountInBaseUnits: BigNumber.BigNumber, txOpts?: TxOpts) => void;
|
||||
}
|
||||
|
||||
export interface TokenRegistryContract {
|
||||
export interface TokenRegistryContract extends ContractInstance {
|
||||
getTokenMetaData: {
|
||||
call: (address: string) => Promise<TokenMetadata>;
|
||||
};
|
||||
@@ -115,6 +124,9 @@ export enum ExchangeContractErrCodes {
|
||||
|
||||
export const ExchangeContractErrs = strEnum([
|
||||
'ORDER_FILL_EXPIRED',
|
||||
'ORDER_CANCEL_EXPIRED',
|
||||
'ORDER_CANCEL_AMOUNT_ZERO',
|
||||
'ORDER_ALREADY_CANCELLED_OR_FILLED',
|
||||
'ORDER_REMAINING_FILL_AMOUNT_ZERO',
|
||||
'ORDER_FILL_ROUNDING_ERROR',
|
||||
'FILL_BALANCE_ALLOWANCE_ERROR',
|
||||
|
@@ -1,5 +1,9 @@
|
||||
import * as _ from 'lodash';
|
||||
import * as BN from 'bn.js';
|
||||
import * as ethABI from 'ethereumjs-abi';
|
||||
import * as ethUtil from 'ethereumjs-util';
|
||||
import {Order, SignedOrder, SolidityTypes} from '../types';
|
||||
import * as BigNumber from 'bignumber.js';
|
||||
|
||||
export const utils = {
|
||||
/**
|
||||
@@ -25,4 +29,28 @@ export const utils = {
|
||||
spawnSwitchErr(name: string, value: any) {
|
||||
return new Error(`Unexpected switch value: ${value} encountered for ${name}`);
|
||||
},
|
||||
getOrderHashHex(order: Order|SignedOrder, exchangeContractAddr: string): string {
|
||||
const orderParts = [
|
||||
{value: exchangeContractAddr, 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: utils.bigNumberToBN(order.makerTokenAmount), type: SolidityTypes.uint256},
|
||||
{value: utils.bigNumberToBN(order.takerTokenAmount), type: SolidityTypes.uint256},
|
||||
{value: utils.bigNumberToBN(order.makerFee), type: SolidityTypes.uint256},
|
||||
{value: utils.bigNumberToBN(order.takerFee), type: SolidityTypes.uint256},
|
||||
{value: utils.bigNumberToBN(order.expirationUnixTimestampSec), type: SolidityTypes.uint256},
|
||||
{value: utils.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;
|
||||
},
|
||||
getCurrentUnixTimestamp(): BigNumber.BigNumber {
|
||||
return new BigNumber(Date.now() / 1000);
|
||||
},
|
||||
};
|
||||
|
@@ -4,13 +4,13 @@ import * as Web3 from 'web3';
|
||||
import * as BigNumber from 'bignumber.js';
|
||||
import {chaiSetup} from './utils/chai_setup';
|
||||
import ChaiBigNumber = require('chai-bignumber');
|
||||
import * as chaiAsPromised from 'chai-as-promised';
|
||||
import promisify = require('es6-promisify');
|
||||
import {web3Factory} from './utils/web3_factory';
|
||||
import {ZeroEx} from '../src/0x.js';
|
||||
import {BlockchainLifecycle} from './utils/blockchain_lifecycle';
|
||||
import {
|
||||
Token,
|
||||
Order,
|
||||
SignedOrder,
|
||||
SubscriptionOpts,
|
||||
ExchangeEvents,
|
||||
@@ -158,7 +158,7 @@ describe('ExchangeWrapper', () => {
|
||||
)).to.be.rejectedWith(ExchangeContractErrs.TRANSACTION_SENDER_IS_NOT_FILL_ORDER_TAKER);
|
||||
});
|
||||
it('should throw when order is expired', async () => {
|
||||
const expirationInPast = new BigNumber(42);
|
||||
const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017
|
||||
const fillableAmount = new BigNumber(5);
|
||||
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
|
||||
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, expirationInPast,
|
||||
@@ -323,6 +323,55 @@ describe('ExchangeWrapper', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('#cancelOrderAsync', () => {
|
||||
let makerTokenAddress: string;
|
||||
let takerTokenAddress: string;
|
||||
let coinbase: string;
|
||||
let makerAddress: string;
|
||||
let takerAddress: string;
|
||||
const fillableAmount = new BigNumber(5);
|
||||
let signedOrder: SignedOrder;
|
||||
let orderHashHex: string;
|
||||
const cancelAmount = new BigNumber(3);
|
||||
beforeEach(async () => {
|
||||
[coinbase, makerAddress, takerAddress] = userAddresses;
|
||||
const [makerToken, takerToken] = tokenUtils.getNonProtocolTokens();
|
||||
makerTokenAddress = makerToken.address;
|
||||
takerTokenAddress = takerToken.address;
|
||||
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
|
||||
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
|
||||
);
|
||||
orderHashHex = await zeroEx.getOrderHashHexAsync(signedOrder);
|
||||
});
|
||||
describe('failed cancels', () => {
|
||||
it('should throw when cancel amount is zero', async () => {
|
||||
const zeroCancelAmount = new BigNumber(0);
|
||||
return expect(zeroEx.exchange.cancelOrderAsync(signedOrder, zeroCancelAmount))
|
||||
.to.be.rejectedWith(ExchangeContractErrs.ORDER_CANCEL_AMOUNT_ZERO);
|
||||
});
|
||||
it('should throw when order is expired', async () => {
|
||||
const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017
|
||||
const expiredSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
|
||||
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, expirationInPast,
|
||||
);
|
||||
orderHashHex = await zeroEx.getOrderHashHexAsync(expiredSignedOrder);
|
||||
return expect(zeroEx.exchange.cancelOrderAsync(expiredSignedOrder, cancelAmount))
|
||||
.to.be.rejectedWith(ExchangeContractErrs.ORDER_CANCEL_EXPIRED);
|
||||
});
|
||||
it('should throw when order is already cancelled or filled', async () => {
|
||||
await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount);
|
||||
return expect(zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount))
|
||||
.to.be.rejectedWith(ExchangeContractErrs.ORDER_ALREADY_CANCELLED_OR_FILLED);
|
||||
});
|
||||
});
|
||||
describe('successful cancels', () => {
|
||||
it('should cancel an order', async () => {
|
||||
await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelAmount);
|
||||
const cancelledAmount = await zeroEx.exchange.getCanceledTakerAmountAsync(orderHashHex);
|
||||
expect(cancelledAmount).to.be.bignumber.equal(cancelAmount);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('tests that require partially filled order', () => {
|
||||
let makerTokenAddress: string;
|
||||
let takerTokenAddress: string;
|
||||
|
@@ -1,9 +1,7 @@
|
||||
import * as _ from 'lodash';
|
||||
import * as BigNumber from 'bignumber.js';
|
||||
import {SignedOrder, Token} from '../../src/types';
|
||||
import {SignedOrder} from '../../src/types';
|
||||
import {ZeroEx} from '../../src/0x.js';
|
||||
import {constants} from './constants';
|
||||
import * as ExchangeArtifacts from '../../src/artifacts/Exchange.json';
|
||||
|
||||
export const orderFactory = {
|
||||
async createSignedOrderAsync(
|
||||
|
Reference in New Issue
Block a user