Merge branch 'master' into addEventSubscriptions
# Conflicts: # src/contract_wrappers/exchange_wrapper.ts # src/types.ts # test/exchange_wrapper_test.ts
This commit is contained in:
commit
ae15425f0b
@ -135,7 +135,7 @@ export class ZeroEx {
|
||||
return senderAccountIfExists;
|
||||
}
|
||||
/**
|
||||
* Computes the orderHash given the order parameters and returns it as a hex encoded string.
|
||||
* Computes the orderHash for a given order and returns it as a hex encoded string.
|
||||
*/
|
||||
public async getOrderHashHexAsync(order: Order|SignedOrder): Promise<string> {
|
||||
const exchangeContractAddr = await this.getExchangeAddressAsync();
|
||||
|
@ -7,7 +7,6 @@ import {
|
||||
ExchangeContract,
|
||||
ExchangeContractErrCodes,
|
||||
ExchangeContractErrs,
|
||||
FillOrderValidationErrs,
|
||||
OrderValues,
|
||||
OrderAddresses,
|
||||
SignedOrder,
|
||||
@ -19,6 +18,7 @@ import {
|
||||
CreateContractEvent,
|
||||
ContractEventObj,
|
||||
EventCallback,
|
||||
ContractResponse,
|
||||
} from '../types';
|
||||
import {assert} from '../utils/assert';
|
||||
import {utils} from '../utils/utils';
|
||||
@ -27,18 +27,17 @@ import * as ExchangeArtifacts from '../artifacts/Exchange.json';
|
||||
import {ecSignatureSchema} from '../schemas/ec_signature_schema';
|
||||
import {signedOrderSchema} from '../schemas/order_schemas';
|
||||
import {SchemaValidator} from '../utils/schema_validator';
|
||||
import {ContractResponse} from '../types';
|
||||
import {constants} from '../utils/constants';
|
||||
import {TokenWrapper} from './token_wrapper';
|
||||
|
||||
export class ExchangeWrapper extends ContractWrapper {
|
||||
private exchangeContractErrCodesToMsg = {
|
||||
[ExchangeContractErrCodes.ERROR_FILL_EXPIRED]: ExchangeContractErrs.ORDER_EXPIRED,
|
||||
[ExchangeContractErrCodes.ERROR_CANCEL_EXPIRED]: ExchangeContractErrs.ORDER_EXPIRED,
|
||||
[ExchangeContractErrCodes.ERROR_FILL_EXPIRED]: ExchangeContractErrs.ORDER_FILL_EXPIRED,
|
||||
[ExchangeContractErrCodes.ERROR_CANCEL_EXPIRED]: ExchangeContractErrs.ORDER_FILL_EXPIRED,
|
||||
[ExchangeContractErrCodes.ERROR_FILL_NO_VALUE]: ExchangeContractErrs.ORDER_REMAINING_FILL_AMOUNT_ZERO,
|
||||
[ExchangeContractErrCodes.ERROR_CANCEL_NO_VALUE]: ExchangeContractErrs.ORDER_REMAINING_FILL_AMOUNT_ZERO,
|
||||
[ExchangeContractErrCodes.ERROR_FILL_TRUNCATION]: ExchangeContractErrs.ORDER_ROUNDING_ERROR,
|
||||
[ExchangeContractErrCodes.ERROR_FILL_BALANCE_ALLOWANCE]: ExchangeContractErrs.ORDER_BALANCE_ALLOWANCE_ERROR,
|
||||
[ExchangeContractErrCodes.ERROR_FILL_TRUNCATION]: ExchangeContractErrs.ORDER_FILL_ROUNDING_ERROR,
|
||||
[ExchangeContractErrCodes.ERROR_FILL_BALANCE_ALLOWANCE]: ExchangeContractErrs.FILL_BALANCE_ALLOWANCE_ERROR,
|
||||
};
|
||||
private exchangeContractIfExists?: ExchangeContract;
|
||||
private exchangeLogEventObjs: ContractEventObj[];
|
||||
@ -112,22 +111,24 @@ export class ExchangeWrapper extends ContractWrapper {
|
||||
return cancelledAmountInBaseUnits;
|
||||
}
|
||||
/**
|
||||
* Fills a signed order with a fillAmount denominated in baseUnits of the taker token. The caller can
|
||||
* decide whether they want the call to throw if the balance/allowance checks fail by setting
|
||||
* shouldCheckTransfer to false. If set to true, the call will fail without throwing, preserving gas costs.
|
||||
* Fills a signed order with a fillAmount denominated in baseUnits of the taker token.
|
||||
* Since the order in which transactions are included in the next block is indeterminate, race-conditions
|
||||
* could arise where a users balance or allowance changes before the fillOrder executes. Because of this,
|
||||
* we allow you to specify `shouldCheckTransfer`. If true, the smart contract will not throw if while
|
||||
* executing, the parties do not have sufficient balances/allowances, preserving gas costs. Setting it to
|
||||
* false forgoes this check and causes the smart contract to throw instead.
|
||||
*/
|
||||
public async fillOrderAsync(signedOrder: SignedOrder, fillTakerAmountInBaseUnits: BigNumber.BigNumber,
|
||||
public async fillOrderAsync(signedOrder: SignedOrder, fillTakerAmount: BigNumber.BigNumber,
|
||||
shouldCheckTransfer: boolean): Promise<void> {
|
||||
assert.doesConformToSchema('signedOrder',
|
||||
SchemaValidator.convertToJSONSchemaCompatibleObject(signedOrder as object),
|
||||
signedOrderSchema);
|
||||
assert.isBigNumber('fillTakerAmountInBaseUnits', fillTakerAmountInBaseUnits);
|
||||
assert.isBigNumber('fillTakerAmount', fillTakerAmount);
|
||||
assert.isBoolean('shouldCheckTransfer', shouldCheckTransfer);
|
||||
|
||||
const senderAddress = await this.web3Wrapper.getSenderAddressOrThrowAsync();
|
||||
await this.validateFillOrderAsync(signedOrder, fillTakerAmountInBaseUnits, senderAddress);
|
||||
|
||||
const exchangeInstance = await this.getExchangeContractAsync();
|
||||
await this.validateFillOrderAndThrowIfInvalidAsync(signedOrder, fillTakerAmount, senderAddress);
|
||||
|
||||
const orderAddresses: OrderAddresses = [
|
||||
signedOrder.maker,
|
||||
@ -147,7 +148,7 @@ export class ExchangeWrapper extends ContractWrapper {
|
||||
const gas = await exchangeInstance.fill.estimateGas(
|
||||
orderAddresses,
|
||||
orderValues,
|
||||
fillTakerAmountInBaseUnits,
|
||||
fillTakerAmount,
|
||||
shouldCheckTransfer,
|
||||
signedOrder.ecSignature.v,
|
||||
signedOrder.ecSignature.r,
|
||||
@ -159,7 +160,7 @@ export class ExchangeWrapper extends ContractWrapper {
|
||||
const response: ContractResponse = await exchangeInstance.fill(
|
||||
orderAddresses,
|
||||
orderValues,
|
||||
fillTakerAmountInBaseUnits,
|
||||
fillTakerAmount,
|
||||
shouldCheckTransfer,
|
||||
signedOrder.ecSignature.v,
|
||||
signedOrder.ecSignature.r,
|
||||
@ -203,17 +204,45 @@ export class ExchangeWrapper extends ContractWrapper {
|
||||
}
|
||||
this.exchangeLogEventObjs = [];
|
||||
}
|
||||
private async validateFillOrderAsync(signedOrder: SignedOrder, fillTakerAmountInBaseUnits: BigNumber.BigNumber,
|
||||
senderAddress: string) {
|
||||
if (fillTakerAmountInBaseUnits.eq(0)) {
|
||||
throw new Error(FillOrderValidationErrs.FILL_AMOUNT_IS_ZERO);
|
||||
private async validateFillOrderAndThrowIfInvalidAsync(signedOrder: SignedOrder,
|
||||
fillTakerAmount: BigNumber.BigNumber,
|
||||
senderAddress: string): Promise<void> {
|
||||
if (fillTakerAmount.eq(0)) {
|
||||
throw new Error(ExchangeContractErrs.ORDER_REMAINING_FILL_AMOUNT_ZERO);
|
||||
}
|
||||
if (signedOrder.taker !== constants.NULL_ADDRESS && signedOrder.taker !== senderAddress) {
|
||||
throw new Error(FillOrderValidationErrs.NOT_A_TAKER);
|
||||
throw new Error(ExchangeContractErrs.TRANSACTION_SENDER_IS_NOT_FILL_ORDER_TAKER);
|
||||
}
|
||||
if (signedOrder.expirationUnixTimestampSec.lessThan(Date.now() / 1000)) {
|
||||
throw new Error(FillOrderValidationErrs.EXPIRED);
|
||||
const currentUnixTimestampSec = Date.now() / 1000;
|
||||
if (signedOrder.expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) {
|
||||
throw new Error(ExchangeContractErrs.ORDER_FILL_EXPIRED);
|
||||
}
|
||||
const zrxTokenAddress = await this.getZRXTokenAddressAsync();
|
||||
await this.validateFillOrderBalancesAndAllowancesAndThrowIfInvalidAsync(signedOrder, fillTakerAmount,
|
||||
senderAddress, zrxTokenAddress);
|
||||
|
||||
const wouldRoundingErrorOccur = await this.isRoundingErrorAsync(
|
||||
signedOrder.takerTokenAmount, fillTakerAmount, signedOrder.makerTokenAmount,
|
||||
);
|
||||
if (wouldRoundingErrorOccur) {
|
||||
throw new Error(ExchangeContractErrs.ORDER_FILL_ROUNDING_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Handling the edge-cases that arise when this happens would require making sure that the user has sufficient
|
||||
* funds to pay both the fees and the transfer amount. We decided to punt on this for now as the contracts
|
||||
* will throw for these edge-cases.
|
||||
* TODO: Throw errors before calling the smart contract for these edge-cases
|
||||
* TODO: in order to minimize the callers gas costs.
|
||||
*/
|
||||
private async validateFillOrderBalancesAndAllowancesAndThrowIfInvalidAsync(signedOrder: SignedOrder,
|
||||
fillTakerAmount: BigNumber.BigNumber,
|
||||
senderAddress: string,
|
||||
zrxTokenAddress: string): Promise<void> {
|
||||
|
||||
const makerBalance = await this.tokenWrapper.getBalanceAsync(signedOrder.makerTokenAddress,
|
||||
signedOrder.maker);
|
||||
const takerBalance = await this.tokenWrapper.getBalanceAsync(signedOrder.takerTokenAddress, senderAddress);
|
||||
@ -221,21 +250,43 @@ export class ExchangeWrapper extends ContractWrapper {
|
||||
signedOrder.maker);
|
||||
const takerAllowance = await this.tokenWrapper.getProxyAllowanceAsync(signedOrder.takerTokenAddress,
|
||||
senderAddress);
|
||||
// How many taker tokens would you get for 1 maker token;
|
||||
const exchangeRate = signedOrder.takerTokenAmount.div(signedOrder.makerTokenAmount);
|
||||
const fillMakerAmountInBaseUnits = fillTakerAmountInBaseUnits.div(exchangeRate);
|
||||
|
||||
if (fillTakerAmountInBaseUnits.greaterThan(takerBalance)) {
|
||||
throw new Error(FillOrderValidationErrs.NOT_ENOUGH_TAKER_BALANCE);
|
||||
// exchangeRate is the price of one maker token denominated in taker tokens
|
||||
const exchangeRate = signedOrder.takerTokenAmount.div(signedOrder.makerTokenAmount);
|
||||
const fillMakerAmountInBaseUnits = fillTakerAmount.div(exchangeRate);
|
||||
|
||||
if (fillTakerAmount.greaterThan(takerBalance)) {
|
||||
throw new Error(ExchangeContractErrs.INSUFFICIENT_TAKER_BALANCE);
|
||||
}
|
||||
if (fillTakerAmountInBaseUnits.greaterThan(takerAllowance)) {
|
||||
throw new Error(FillOrderValidationErrs.NOT_ENOUGH_TAKER_ALLOWANCE);
|
||||
if (fillTakerAmount.greaterThan(takerAllowance)) {
|
||||
throw new Error(ExchangeContractErrs.INSUFFICIENT_TAKER_ALLOWANCE);
|
||||
}
|
||||
if (fillMakerAmountInBaseUnits.greaterThan(makerBalance)) {
|
||||
throw new Error(FillOrderValidationErrs.NOT_ENOUGH_MAKER_BALANCE);
|
||||
throw new Error(ExchangeContractErrs.INSUFFICIENT_MAKER_BALANCE);
|
||||
}
|
||||
if (fillMakerAmountInBaseUnits.greaterThan(makerAllowance)) {
|
||||
throw new Error(FillOrderValidationErrs.NOT_ENOUGH_MAKER_ALLOWANCE);
|
||||
throw new Error(ExchangeContractErrs.INSUFFICIENT_MAKER_ALLOWANCE);
|
||||
}
|
||||
|
||||
const makerFeeBalance = await this.tokenWrapper.getBalanceAsync(zrxTokenAddress,
|
||||
signedOrder.maker);
|
||||
const takerFeeBalance = await this.tokenWrapper.getBalanceAsync(zrxTokenAddress, senderAddress);
|
||||
const makerFeeAllowance = await this.tokenWrapper.getProxyAllowanceAsync(zrxTokenAddress,
|
||||
signedOrder.maker);
|
||||
const takerFeeAllowance = await this.tokenWrapper.getProxyAllowanceAsync(zrxTokenAddress,
|
||||
senderAddress);
|
||||
|
||||
if (signedOrder.takerFee.greaterThan(takerFeeBalance)) {
|
||||
throw new Error(ExchangeContractErrs.INSUFFICIENT_TAKER_FEE_BALANCE);
|
||||
}
|
||||
if (signedOrder.takerFee.greaterThan(takerFeeAllowance)) {
|
||||
throw new Error(ExchangeContractErrs.INSUFFICIENT_TAKER_FEE_ALLOWANCE);
|
||||
}
|
||||
if (signedOrder.makerFee.greaterThan(makerFeeBalance)) {
|
||||
throw new Error(ExchangeContractErrs.INSUFFICIENT_MAKER_FEE_BALANCE);
|
||||
}
|
||||
if (signedOrder.makerFee.greaterThan(makerFeeAllowance)) {
|
||||
throw new Error(ExchangeContractErrs.INSUFFICIENT_MAKER_FEE_ALLOWANCE);
|
||||
}
|
||||
}
|
||||
private throwErrorLogsAsErrors(logs: ContractEvent[]): void {
|
||||
@ -246,6 +297,18 @@ export class ExchangeWrapper extends ContractWrapper {
|
||||
throw new Error(errMessage);
|
||||
}
|
||||
}
|
||||
private async isRoundingErrorAsync(takerTokenAmount: BigNumber.BigNumber,
|
||||
fillTakerAmount: BigNumber.BigNumber,
|
||||
makerTokenAmount: BigNumber.BigNumber): Promise<boolean> {
|
||||
const exchangeInstance = await this.getExchangeContractAsync();
|
||||
const senderAddress = await this.web3Wrapper.getSenderAddressOrThrowAsync();
|
||||
const isRoundingError = await exchangeInstance.isRoundingError.call(
|
||||
takerTokenAmount, fillTakerAmount, makerTokenAmount, {
|
||||
from: senderAddress,
|
||||
},
|
||||
);
|
||||
return isRoundingError;
|
||||
}
|
||||
private async getExchangeContractAsync(): Promise<ExchangeContract> {
|
||||
if (!_.isUndefined(this.exchangeContractIfExists)) {
|
||||
return this.exchangeContractIfExists;
|
||||
@ -254,4 +317,8 @@ export class ExchangeWrapper extends ContractWrapper {
|
||||
this.exchangeContractIfExists = contractInstance as ExchangeContract;
|
||||
return this.exchangeContractIfExists;
|
||||
}
|
||||
private async getZRXTokenAddressAsync(): Promise<string> {
|
||||
const exchangeInstance = await this.getExchangeContractAsync();
|
||||
return exchangeInstance.ZRX.call();
|
||||
}
|
||||
}
|
||||
|
35
src/types.ts
35
src/types.ts
@ -15,6 +15,7 @@ export const ZeroExError = strEnum([
|
||||
'USER_HAS_NO_ASSOCIATED_ADDRESSES',
|
||||
'INVALID_SIGNATURE',
|
||||
'CONTRACT_NOT_DEPLOYED_ON_NETWORK',
|
||||
'ZRX_NOT_IN_TOKEN_REGISTRY',
|
||||
]);
|
||||
export type ZeroExError = keyof typeof ZeroExError;
|
||||
|
||||
@ -37,6 +38,10 @@ export interface ExchangeContract {
|
||||
getUnavailableValueT: {
|
||||
call: (orderHash: string) => BigNumber.BigNumber;
|
||||
};
|
||||
isRoundingError: {
|
||||
call: (takerTokenAmount: BigNumber.BigNumber, fillTakerAmount: BigNumber.BigNumber,
|
||||
makerTokenAmount: BigNumber.BigNumber, txOpts: TxOpts) => Promise<boolean>;
|
||||
};
|
||||
fill: {
|
||||
(orderAddresses: OrderAddresses, orderValues: OrderValues, fillAmount: BigNumber.BigNumber,
|
||||
shouldCheckTransfer: boolean, v: number, r: string, s: string, txOpts: TxOpts): ContractResponse;
|
||||
@ -65,6 +70,9 @@ export interface ExchangeContract {
|
||||
LogFill: CreateContractEvent;
|
||||
LogCancel: CreateContractEvent;
|
||||
LogError: CreateContractEvent;
|
||||
ZRX: {
|
||||
call: () => Promise<string>;
|
||||
};
|
||||
}
|
||||
|
||||
export interface TokenContract {
|
||||
@ -103,24 +111,23 @@ export enum ExchangeContractErrCodes {
|
||||
}
|
||||
|
||||
export const ExchangeContractErrs = strEnum([
|
||||
'ORDER_EXPIRED',
|
||||
'ORDER_FILL_EXPIRED',
|
||||
'ORDER_REMAINING_FILL_AMOUNT_ZERO',
|
||||
'ORDER_ROUNDING_ERROR',
|
||||
'ORDER_BALANCE_ALLOWANCE_ERROR',
|
||||
'ORDER_FILL_ROUNDING_ERROR',
|
||||
'FILL_BALANCE_ALLOWANCE_ERROR',
|
||||
'INSUFFICIENT_TAKER_BALANCE',
|
||||
'INSUFFICIENT_TAKER_ALLOWANCE',
|
||||
'INSUFFICIENT_MAKER_BALANCE',
|
||||
'INSUFFICIENT_MAKER_ALLOWANCE',
|
||||
'INSUFFICIENT_TAKER_FEE_BALANCE',
|
||||
'INSUFFICIENT_TAKER_FEE_ALLOWANCE',
|
||||
'INSUFFICIENT_MAKER_FEE_BALANCE',
|
||||
'INSUFFICIENT_MAKER_FEE_ALLOWANCE',
|
||||
'TRANSACTION_SENDER_IS_NOT_FILL_ORDER_TAKER',
|
||||
|
||||
]);
|
||||
export type ExchangeContractErrs = keyof typeof ExchangeContractErrs;
|
||||
|
||||
export const FillOrderValidationErrs = strEnum([
|
||||
'FILL_AMOUNT_IS_ZERO',
|
||||
'NOT_A_TAKER',
|
||||
'EXPIRED',
|
||||
'NOT_ENOUGH_TAKER_BALANCE',
|
||||
'NOT_ENOUGH_TAKER_ALLOWANCE',
|
||||
'NOT_ENOUGH_MAKER_BALANCE',
|
||||
'NOT_ENOUGH_MAKER_ALLOWANCE',
|
||||
]);
|
||||
export type FillOrderValidationErrs = keyof typeof FillOrderValidationErrs;
|
||||
|
||||
export interface ContractResponse {
|
||||
logs: ContractEvent[];
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import {tokenSchema} from '../schemas/token_schema';
|
||||
export class SchemaValidator {
|
||||
private validator: Validator;
|
||||
// In order to validate a complex JS object using jsonschema, we must replace any complex
|
||||
// sub-types (e.g BigNumber) with a simpler string represenation. Since BigNumber and other
|
||||
// sub-types (e.g BigNumber) with a simpler string representation. Since BigNumber and other
|
||||
// complex types implement the `toString` method, we can stringify the object and
|
||||
// then parse it. The resultant object can then be checked using jsonschema.
|
||||
public static convertToJSONSchemaCompatibleObject(obj: object): object {
|
||||
|
@ -9,17 +9,17 @@ import promisify = require('es6-promisify');
|
||||
import {web3Factory} from './utils/web3_factory';
|
||||
import {ZeroEx} from '../src/0x.js';
|
||||
import {BlockchainLifecycle} from './utils/blockchain_lifecycle';
|
||||
import {orderFactory} from './utils/order_factory';
|
||||
import {
|
||||
FillOrderValidationErrs,
|
||||
Token,
|
||||
SignedOrder,
|
||||
SubscriptionOpts,
|
||||
ExchangeEvents,
|
||||
ContractEvent,
|
||||
DoneCallback,
|
||||
ExchangeContractErrs,
|
||||
} from '../src/types';
|
||||
import {FillScenarios} from './utils/fill_scenarios';
|
||||
import {TokenUtils} from './utils/token_utils';
|
||||
|
||||
chai.use(dirtyChai);
|
||||
chai.use(ChaiBigNumber());
|
||||
@ -29,17 +29,21 @@ const blockchainLifecycle = new BlockchainLifecycle();
|
||||
const NON_EXISTENT_ORDER_HASH = '0x79370342234e7acd6bbeac335bd3bb1d368383294b64b8160a00f4060e4d3777';
|
||||
|
||||
describe('ExchangeWrapper', () => {
|
||||
let web3: Web3;
|
||||
let zeroEx: ZeroEx;
|
||||
let userAddresses: string[];
|
||||
let web3: Web3;
|
||||
let tokenUtils: TokenUtils;
|
||||
let tokens: Token[];
|
||||
let fillScenarios: FillScenarios;
|
||||
let zrxTokenAddress: string;
|
||||
before(async () => {
|
||||
web3 = web3Factory.create();
|
||||
zeroEx = new ZeroEx(web3);
|
||||
userAddresses = await promisify(web3.eth.getAccounts)();
|
||||
tokens = await zeroEx.tokenRegistry.getTokensAsync();
|
||||
fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens);
|
||||
tokenUtils = new TokenUtils(tokens);
|
||||
zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address;
|
||||
fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens, zrxTokenAddress);
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
@ -120,14 +124,16 @@ describe('ExchangeWrapper', () => {
|
||||
describe('#fillOrderAsync', () => {
|
||||
let makerTokenAddress: string;
|
||||
let takerTokenAddress: string;
|
||||
let coinBase: string;
|
||||
let coinbase: string;
|
||||
let makerAddress: string;
|
||||
let takerAddress: string;
|
||||
const fillTakerAmountInBaseUnits = new BigNumber(5);
|
||||
let feeRecipient: string;
|
||||
const fillTakerAmount = new BigNumber(5);
|
||||
const shouldCheckTransfer = false;
|
||||
before('fetch tokens', async () => {
|
||||
[coinBase, makerAddress, takerAddress] = userAddresses;
|
||||
const [makerToken, takerToken] = tokens;
|
||||
before(async () => {
|
||||
[coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses;
|
||||
tokens = await zeroEx.tokenRegistry.getTokensAsync();
|
||||
const [makerToken, takerToken] = tokenUtils.getNonProtocolTokens();
|
||||
makerTokenAddress = makerToken.address;
|
||||
takerTokenAddress = takerToken.address;
|
||||
});
|
||||
@ -137,92 +143,149 @@ describe('ExchangeWrapper', () => {
|
||||
describe('failed fills', () => {
|
||||
it('should throw when the fill amount is zero', async () => {
|
||||
const fillableAmount = new BigNumber(5);
|
||||
const signedOrder = await fillScenarios.createAFillableSignedOrderAsync(
|
||||
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
|
||||
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
|
||||
);
|
||||
const zeroFillAmount = new BigNumber(0);
|
||||
zeroEx.setTransactionSenderAccount(takerAddress);
|
||||
return expect(zeroEx.exchange.fillOrderAsync(
|
||||
signedOrder, zeroFillAmount, shouldCheckTransfer,
|
||||
)).to.be.rejectedWith(FillOrderValidationErrs.FILL_AMOUNT_IS_ZERO);
|
||||
)).to.be.rejectedWith(ExchangeContractErrs.ORDER_REMAINING_FILL_AMOUNT_ZERO);
|
||||
});
|
||||
it('should throw when sender is not a taker', async () => {
|
||||
const fillableAmount = new BigNumber(5);
|
||||
const signedOrder = await fillScenarios.createAFillableSignedOrderAsync(
|
||||
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
|
||||
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
|
||||
);
|
||||
return expect(zeroEx.exchange.fillOrderAsync(
|
||||
signedOrder, fillTakerAmountInBaseUnits, shouldCheckTransfer,
|
||||
)).to.be.rejectedWith(FillOrderValidationErrs.NOT_A_TAKER);
|
||||
signedOrder, fillTakerAmount, shouldCheckTransfer,
|
||||
)).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 fillableAmount = new BigNumber(5);
|
||||
const signedOrder = await fillScenarios.createAFillableSignedOrderAsync(
|
||||
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
|
||||
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, expirationInPast,
|
||||
);
|
||||
zeroEx.setTransactionSenderAccount(takerAddress);
|
||||
return expect(zeroEx.exchange.fillOrderAsync(
|
||||
signedOrder, fillTakerAmountInBaseUnits, shouldCheckTransfer,
|
||||
)).to.be.rejectedWith(FillOrderValidationErrs.EXPIRED);
|
||||
signedOrder, fillTakerAmount, shouldCheckTransfer,
|
||||
)).to.be.rejectedWith(ExchangeContractErrs.ORDER_FILL_EXPIRED);
|
||||
});
|
||||
describe('should throw when not enough balance or allowance to fulfill the order', () => {
|
||||
const fillableAmount = new BigNumber(5);
|
||||
const balanceToSubtractFromMaker = new BigNumber(3);
|
||||
const lackingAllowance = new BigNumber(3);
|
||||
let signedOrder: SignedOrder;
|
||||
beforeEach('create fillable signed order', async () => {
|
||||
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
|
||||
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
|
||||
);
|
||||
});
|
||||
it('should throw when taker balance is less than fill amount', async () => {
|
||||
const fillableAmount = new BigNumber(5);
|
||||
const signedOrder = await fillScenarios.createAFillableSignedOrderAsync(
|
||||
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
|
||||
await zeroEx.token.transferAsync(
|
||||
takerTokenAddress, takerAddress, coinbase, balanceToSubtractFromMaker,
|
||||
);
|
||||
zeroEx.setTransactionSenderAccount(takerAddress);
|
||||
const moreThanTheBalance = new BigNumber(6);
|
||||
return expect(zeroEx.exchange.fillOrderAsync(
|
||||
signedOrder, moreThanTheBalance, shouldCheckTransfer,
|
||||
)).to.be.rejectedWith(FillOrderValidationErrs.NOT_ENOUGH_TAKER_BALANCE);
|
||||
signedOrder, fillTakerAmount, shouldCheckTransfer,
|
||||
)).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_TAKER_BALANCE);
|
||||
});
|
||||
it('should throw when taker allowance is less than fill amount', async () => {
|
||||
const fillableAmount = new BigNumber(5);
|
||||
const signedOrder = await fillScenarios.createAFillableSignedOrderAsync(
|
||||
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
|
||||
);
|
||||
const newAllowanceWhichIsLessThanFillAmount = fillTakerAmountInBaseUnits.minus(1);
|
||||
const newAllowanceWhichIsLessThanFillAmount = fillTakerAmount.minus(lackingAllowance);
|
||||
await zeroEx.token.setProxyAllowanceAsync(takerTokenAddress, takerAddress,
|
||||
newAllowanceWhichIsLessThanFillAmount);
|
||||
zeroEx.setTransactionSenderAccount(takerAddress);
|
||||
return expect(zeroEx.exchange.fillOrderAsync(
|
||||
signedOrder, fillTakerAmountInBaseUnits, shouldCheckTransfer,
|
||||
)).to.be.rejectedWith(FillOrderValidationErrs.NOT_ENOUGH_TAKER_ALLOWANCE);
|
||||
signedOrder, fillTakerAmount, shouldCheckTransfer,
|
||||
)).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_TAKER_ALLOWANCE);
|
||||
});
|
||||
it('should throw when maker balance is less than maker fill amount', async () => {
|
||||
const fillableAmount = new BigNumber(5);
|
||||
const signedOrder = await fillScenarios.createAFillableSignedOrderAsync(
|
||||
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
|
||||
await zeroEx.token.transferAsync(
|
||||
makerTokenAddress, makerAddress, coinbase, balanceToSubtractFromMaker,
|
||||
);
|
||||
const lackingMakerBalance = new BigNumber(3);
|
||||
await zeroEx.token.transferAsync(makerTokenAddress, makerAddress, coinBase, lackingMakerBalance);
|
||||
zeroEx.setTransactionSenderAccount(takerAddress);
|
||||
return expect(zeroEx.exchange.fillOrderAsync(
|
||||
signedOrder, fillTakerAmountInBaseUnits, shouldCheckTransfer,
|
||||
)).to.be.rejectedWith(FillOrderValidationErrs.NOT_ENOUGH_MAKER_BALANCE);
|
||||
signedOrder, fillTakerAmount, shouldCheckTransfer,
|
||||
)).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_MAKER_BALANCE);
|
||||
});
|
||||
it('should throw when maker allowance is less than maker fill amount', async () => {
|
||||
const fillableAmount = new BigNumber(5);
|
||||
const signedOrder = await fillScenarios.createAFillableSignedOrderAsync(
|
||||
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
|
||||
);
|
||||
const newAllowanceWhichIsLessThanFillAmount = fillTakerAmountInBaseUnits.minus(1);
|
||||
const newAllowanceWhichIsLessThanFillAmount = fillTakerAmount.minus(lackingAllowance);
|
||||
await zeroEx.token.setProxyAllowanceAsync(makerTokenAddress, makerAddress,
|
||||
newAllowanceWhichIsLessThanFillAmount);
|
||||
zeroEx.setTransactionSenderAccount(takerAddress);
|
||||
return expect(zeroEx.exchange.fillOrderAsync(
|
||||
signedOrder, fillTakerAmountInBaseUnits, shouldCheckTransfer,
|
||||
)).to.be.rejectedWith(FillOrderValidationErrs.NOT_ENOUGH_MAKER_ALLOWANCE);
|
||||
signedOrder, fillTakerAmount, shouldCheckTransfer,
|
||||
)).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_MAKER_ALLOWANCE);
|
||||
});
|
||||
});
|
||||
it('should throw when there a rounding error would have occurred', async () => {
|
||||
const makerAmount = new BigNumber(3);
|
||||
const takerAmount = new BigNumber(5);
|
||||
const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
|
||||
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress,
|
||||
makerAmount, takerAmount,
|
||||
);
|
||||
const fillTakerAmountThatCausesRoundingError = new BigNumber(3);
|
||||
zeroEx.setTransactionSenderAccount(takerAddress);
|
||||
return expect(zeroEx.exchange.fillOrderAsync(
|
||||
signedOrder, fillTakerAmountThatCausesRoundingError, shouldCheckTransfer,
|
||||
)).to.be.rejectedWith(ExchangeContractErrs.ORDER_FILL_ROUNDING_ERROR);
|
||||
});
|
||||
describe('should throw when not enough balance or allowance to pay fees', () => {
|
||||
const fillableAmount = new BigNumber(5);
|
||||
const makerFee = new BigNumber(2);
|
||||
const takerFee = new BigNumber(2);
|
||||
let signedOrder: SignedOrder;
|
||||
beforeEach('setup', async () => {
|
||||
signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
|
||||
makerTokenAddress, takerTokenAddress, makerFee, takerFee,
|
||||
makerAddress, takerAddress, fillableAmount, feeRecipient,
|
||||
);
|
||||
zeroEx.setTransactionSenderAccount(takerAddress);
|
||||
});
|
||||
it('should throw when maker doesn\'t have enough balance to pay fees', async () => {
|
||||
const balanceToSubtractFromMaker = new BigNumber(1);
|
||||
await zeroEx.token.transferAsync(
|
||||
zrxTokenAddress, makerAddress, coinbase, balanceToSubtractFromMaker,
|
||||
);
|
||||
return expect(zeroEx.exchange.fillOrderAsync(
|
||||
signedOrder, fillTakerAmount, shouldCheckTransfer,
|
||||
)).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_MAKER_FEE_BALANCE);
|
||||
});
|
||||
it('should throw when maker doesn\'t have enough allowance to pay fees', async () => {
|
||||
const newAllowanceWhichIsLessThanFees = makerFee.minus(1);
|
||||
await zeroEx.token.setProxyAllowanceAsync(zrxTokenAddress, makerAddress,
|
||||
newAllowanceWhichIsLessThanFees);
|
||||
return expect(zeroEx.exchange.fillOrderAsync(
|
||||
signedOrder, fillTakerAmount, shouldCheckTransfer,
|
||||
)).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_MAKER_FEE_ALLOWANCE);
|
||||
});
|
||||
it('should throw when taker doesn\'t have enough balance to pay fees', async () => {
|
||||
const balanceToSubtractFromTaker = new BigNumber(1);
|
||||
await zeroEx.token.transferAsync(
|
||||
zrxTokenAddress, takerAddress, coinbase, balanceToSubtractFromTaker,
|
||||
);
|
||||
return expect(zeroEx.exchange.fillOrderAsync(
|
||||
signedOrder, fillTakerAmount, shouldCheckTransfer,
|
||||
)).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_TAKER_FEE_BALANCE);
|
||||
});
|
||||
it('should throw when taker doesn\'t have enough allowance to pay fees', async () => {
|
||||
const newAllowanceWhichIsLessThanFees = makerFee.minus(1);
|
||||
await zeroEx.token.setProxyAllowanceAsync(zrxTokenAddress, takerAddress,
|
||||
newAllowanceWhichIsLessThanFees);
|
||||
return expect(zeroEx.exchange.fillOrderAsync(
|
||||
signedOrder, fillTakerAmount, shouldCheckTransfer,
|
||||
)).to.be.rejectedWith(ExchangeContractErrs.INSUFFICIENT_TAKER_FEE_ALLOWANCE);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('successful fills', () => {
|
||||
it('should fill the valid order', async () => {
|
||||
it('should fill a valid order', async () => {
|
||||
const fillableAmount = new BigNumber(5);
|
||||
const signedOrder = await fillScenarios.createAFillableSignedOrderAsync(
|
||||
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
|
||||
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
|
||||
);
|
||||
|
||||
expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress))
|
||||
.to.be.bignumber.equal(fillableAmount);
|
||||
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress))
|
||||
@ -232,19 +295,19 @@ describe('ExchangeWrapper', () => {
|
||||
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress))
|
||||
.to.be.bignumber.equal(fillableAmount);
|
||||
zeroEx.setTransactionSenderAccount(takerAddress);
|
||||
await zeroEx.exchange.fillOrderAsync(signedOrder, fillTakerAmountInBaseUnits, shouldCheckTransfer);
|
||||
await zeroEx.exchange.fillOrderAsync(signedOrder, fillTakerAmount, shouldCheckTransfer);
|
||||
expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, makerAddress))
|
||||
.to.be.bignumber.equal(fillableAmount.minus(fillTakerAmountInBaseUnits));
|
||||
.to.be.bignumber.equal(fillableAmount.minus(fillTakerAmount));
|
||||
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, makerAddress))
|
||||
.to.be.bignumber.equal(fillTakerAmountInBaseUnits);
|
||||
.to.be.bignumber.equal(fillTakerAmount);
|
||||
expect(await zeroEx.token.getBalanceAsync(makerTokenAddress, takerAddress))
|
||||
.to.be.bignumber.equal(fillTakerAmountInBaseUnits);
|
||||
.to.be.bignumber.equal(fillTakerAmount);
|
||||
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress))
|
||||
.to.be.bignumber.equal(fillableAmount.minus(fillTakerAmountInBaseUnits));
|
||||
.to.be.bignumber.equal(fillableAmount.minus(fillTakerAmount));
|
||||
});
|
||||
it('should partially fill the valid order', async () => {
|
||||
const fillableAmount = new BigNumber(5);
|
||||
const signedOrder = await fillScenarios.createAFillableSignedOrderAsync(
|
||||
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
|
||||
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
|
||||
);
|
||||
const partialFillAmount = new BigNumber(3);
|
||||
@ -259,6 +322,19 @@ describe('ExchangeWrapper', () => {
|
||||
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress))
|
||||
.to.be.bignumber.equal(fillableAmount.minus(partialFillAmount));
|
||||
});
|
||||
it('should fill the valid orders with fees', async () => {
|
||||
const fillableAmount = new BigNumber(5);
|
||||
const makerFee = new BigNumber(1);
|
||||
const takerFee = new BigNumber(2);
|
||||
const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
|
||||
makerTokenAddress, takerTokenAddress, makerFee, takerFee,
|
||||
makerAddress, takerAddress, fillableAmount, feeRecipient,
|
||||
);
|
||||
zeroEx.setTransactionSenderAccount(takerAddress);
|
||||
await zeroEx.exchange.fillOrderAsync(signedOrder, fillTakerAmount, shouldCheckTransfer);
|
||||
expect(await zeroEx.token.getBalanceAsync(zrxTokenAddress, feeRecipient))
|
||||
.to.be.bignumber.equal(makerFee.plus(takerFee));
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('tests that require partially filled order', () => {
|
||||
@ -332,20 +408,20 @@ describe('ExchangeWrapper', () => {
|
||||
const shouldCheckTransfer = false;
|
||||
let makerTokenAddress: string;
|
||||
let takerTokenAddress: string;
|
||||
let coinBase: string;
|
||||
let coinbase: string;
|
||||
let takerAddress: string;
|
||||
let makerAddress: string;
|
||||
let fillableAmount: BigNumber.BigNumber;
|
||||
let signedOrder: SignedOrder;
|
||||
before(() => {
|
||||
[coinBase, makerAddress, takerAddress] = userAddresses;
|
||||
[coinbase, makerAddress, takerAddress] = userAddresses;
|
||||
const [makerToken, takerToken] = tokens;
|
||||
makerTokenAddress = makerToken.address;
|
||||
takerTokenAddress = takerToken.address;
|
||||
});
|
||||
beforeEach(async () => {
|
||||
fillableAmount = new BigNumber(5);
|
||||
signedOrder = await fillScenarios.createAFillableSignedOrderAsync(
|
||||
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
|
||||
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
|
||||
);
|
||||
});
|
||||
|
@ -2,35 +2,54 @@ import * as BigNumber from 'bignumber.js';
|
||||
import {ZeroEx} from '../../src/0x.js';
|
||||
import {Token, SignedOrder} from '../../src/types';
|
||||
import {orderFactory} from '../utils/order_factory';
|
||||
import {constants} from './constants';
|
||||
|
||||
export class FillScenarios {
|
||||
private zeroEx: ZeroEx;
|
||||
private userAddresses: string[];
|
||||
private tokens: Token[];
|
||||
private coinBase: string;
|
||||
constructor(zeroEx: ZeroEx, userAddresses: string[], tokens: Token[]) {
|
||||
private coinbase: string;
|
||||
private zrxTokenAddress: string;
|
||||
constructor(zeroEx: ZeroEx, userAddresses: string[], tokens: Token[], zrxTokenAddress: string) {
|
||||
this.zeroEx = zeroEx;
|
||||
this.userAddresses = userAddresses;
|
||||
this.tokens = tokens;
|
||||
this.coinBase = userAddresses[0];
|
||||
this.coinbase = userAddresses[0];
|
||||
this.zrxTokenAddress = zrxTokenAddress;
|
||||
}
|
||||
public async createAFillableSignedOrderAsync(makerTokenAddress: string, takerTokenAddress: string,
|
||||
public async createFillableSignedOrderAsync(makerTokenAddress: string, takerTokenAddress: string,
|
||||
makerAddress: string, takerAddress: string,
|
||||
fillableAmount: BigNumber.BigNumber,
|
||||
expirationUnixTimestampSec?: BigNumber.BigNumber):
|
||||
Promise<SignedOrder> {
|
||||
await this.zeroEx.token.transferAsync(makerTokenAddress, this.coinBase, makerAddress, fillableAmount);
|
||||
await this.zeroEx.token.setProxyAllowanceAsync(makerTokenAddress, makerAddress, fillableAmount);
|
||||
await this.zeroEx.token.transferAsync(takerTokenAddress, this.coinBase, takerAddress, fillableAmount);
|
||||
await this.zeroEx.token.setProxyAllowanceAsync(takerTokenAddress, takerAddress, fillableAmount);
|
||||
|
||||
const transactionSenderAccount = await this.zeroEx.getTransactionSenderAccountIfExistsAsync();
|
||||
this.zeroEx.setTransactionSenderAccount(makerAddress);
|
||||
const signedOrder = await orderFactory.createSignedOrderAsync(this.zeroEx, makerAddress,
|
||||
takerAddress, fillableAmount, makerTokenAddress, fillableAmount, takerTokenAddress,
|
||||
expirationUnixTimestampSec);
|
||||
this.zeroEx.setTransactionSenderAccount(transactionSenderAccount as string);
|
||||
return signedOrder;
|
||||
return this.createAsymmetricFillableSignedOrderAsync(
|
||||
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress,
|
||||
fillableAmount, fillableAmount, expirationUnixTimestampSec,
|
||||
);
|
||||
}
|
||||
public async createFillableSignedOrderWithFeesAsync(
|
||||
makerTokenAddress: string, takerTokenAddress: string,
|
||||
makerFee: BigNumber.BigNumber, takerFee: BigNumber.BigNumber,
|
||||
makerAddress: string, takerAddress: string,
|
||||
fillableAmount: BigNumber.BigNumber,
|
||||
feeRecepient: string, expirationUnixTimestampSec?: BigNumber.BigNumber,
|
||||
): Promise<SignedOrder> {
|
||||
return this.createAsymmetricFillableSignedOrderWithFeesAsync(
|
||||
makerTokenAddress, takerTokenAddress, makerFee, takerFee, makerAddress, takerAddress,
|
||||
fillableAmount, fillableAmount, feeRecepient, expirationUnixTimestampSec,
|
||||
);
|
||||
}
|
||||
public async createAsymmetricFillableSignedOrderAsync(
|
||||
makerTokenAddress: string, takerTokenAddress: string, makerAddress: string, takerAddress: string,
|
||||
makerFillableAmount: BigNumber.BigNumber, takerFillableAmount: BigNumber.BigNumber,
|
||||
expirationUnixTimestampSec?: BigNumber.BigNumber): Promise<SignedOrder> {
|
||||
const makerFee = new BigNumber(0);
|
||||
const takerFee = new BigNumber(0);
|
||||
const feeRecepient = constants.NULL_ADDRESS;
|
||||
return this.createAsymmetricFillableSignedOrderWithFeesAsync(
|
||||
makerTokenAddress, takerTokenAddress, makerFee, takerFee, makerAddress, takerAddress,
|
||||
makerFillableAmount, takerFillableAmount, feeRecepient, expirationUnixTimestampSec,
|
||||
);
|
||||
}
|
||||
public async createPartiallyFilledSignedOrderAsync(makerTokenAddress: string, takerTokenAddress: string,
|
||||
takerAddress: string, fillableAmount: BigNumber.BigNumber,
|
||||
@ -41,8 +60,10 @@ export class FillScenarios {
|
||||
await this.zeroEx.token.transferAsync(takerTokenAddress, makerAddress, takerAddress, fillableAmount);
|
||||
await this.zeroEx.token.setProxyAllowanceAsync(takerTokenAddress, takerAddress, fillableAmount);
|
||||
|
||||
const signedOrder = await orderFactory.createSignedOrderAsync(this.zeroEx, makerAddress,
|
||||
takerAddress, fillableAmount, makerTokenAddress, fillableAmount, takerTokenAddress);
|
||||
const signedOrder = await this.createAsymmetricFillableSignedOrderAsync(
|
||||
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress,
|
||||
fillableAmount, fillableAmount,
|
||||
);
|
||||
|
||||
this.zeroEx.setTransactionSenderAccount(takerAddress);
|
||||
const shouldCheckTransfer = false;
|
||||
@ -52,4 +73,34 @@ export class FillScenarios {
|
||||
this.zeroEx.setTransactionSenderAccount(prevSenderAccount as string);
|
||||
return signedOrder;
|
||||
}
|
||||
private async createAsymmetricFillableSignedOrderWithFeesAsync(
|
||||
makerTokenAddress: string, takerTokenAddress: string,
|
||||
makerFee: BigNumber.BigNumber, takerFee: BigNumber.BigNumber,
|
||||
makerAddress: string, takerAddress: string,
|
||||
makerFillableAmount: BigNumber.BigNumber, takerFillableAmount: BigNumber.BigNumber,
|
||||
feeRecepient: string, expirationUnixTimestampSec?: BigNumber.BigNumber): Promise<SignedOrder> {
|
||||
await this.zeroEx.token.transferAsync(makerTokenAddress, this.coinbase, makerAddress, makerFillableAmount);
|
||||
await this.zeroEx.token.setProxyAllowanceAsync(makerTokenAddress, makerAddress, makerFillableAmount);
|
||||
await this.zeroEx.token.transferAsync(takerTokenAddress, this.coinbase, takerAddress, takerFillableAmount);
|
||||
await this.zeroEx.token.setProxyAllowanceAsync(takerTokenAddress, takerAddress, takerFillableAmount);
|
||||
|
||||
if (!makerFee.isZero()) {
|
||||
await this.zeroEx.token.transferAsync(this.zrxTokenAddress, this.coinbase, makerAddress, makerFee);
|
||||
await this.zeroEx.token.setProxyAllowanceAsync(this.zrxTokenAddress, makerAddress, makerFee);
|
||||
}
|
||||
if (!takerFee.isZero()) {
|
||||
await this.zeroEx.token.transferAsync(this.zrxTokenAddress, this.coinbase, takerAddress, takerFee);
|
||||
await this.zeroEx.token.setProxyAllowanceAsync(this.zrxTokenAddress, takerAddress, takerFee);
|
||||
}
|
||||
|
||||
const prevTransactionSenderAccount = await this.zeroEx.getTransactionSenderAccountIfExistsAsync();
|
||||
this.zeroEx.setTransactionSenderAccount(makerAddress);
|
||||
const signedOrder = await orderFactory.createSignedOrderAsync(this.zeroEx,
|
||||
makerAddress, takerAddress, makerFee, takerFee,
|
||||
makerFillableAmount, makerTokenAddress, takerFillableAmount, takerTokenAddress,
|
||||
feeRecepient, expirationUnixTimestampSec);
|
||||
// We re-set the transactionSender to avoid introducing side-effects
|
||||
this.zeroEx.setTransactionSenderAccount(prevTransactionSenderAccount as string);
|
||||
return signedOrder;
|
||||
}
|
||||
}
|
||||
|
@ -10,10 +10,13 @@ export const orderFactory = {
|
||||
zeroEx: ZeroEx,
|
||||
maker: string,
|
||||
taker: string,
|
||||
makerTokenAmount: BigNumber.BigNumber|number,
|
||||
makerFee: BigNumber.BigNumber,
|
||||
takerFee: BigNumber.BigNumber,
|
||||
makerTokenAmount: BigNumber.BigNumber,
|
||||
makerTokenAddress: string,
|
||||
takerTokenAmount: BigNumber.BigNumber|number,
|
||||
takerTokenAmount: BigNumber.BigNumber,
|
||||
takerTokenAddress: string,
|
||||
feeRecipient: string,
|
||||
expirationUnixTimestampSec?: BigNumber.BigNumber): Promise<SignedOrder> {
|
||||
const defaultExpirationUnixTimestampSec = new BigNumber(2524604400); // Close to infinite
|
||||
expirationUnixTimestampSec = _.isUndefined(expirationUnixTimestampSec) ?
|
||||
@ -22,14 +25,14 @@ export const orderFactory = {
|
||||
const order = {
|
||||
maker,
|
||||
taker,
|
||||
makerFee: new BigNumber(0),
|
||||
takerFee: new BigNumber(0),
|
||||
makerTokenAmount: _.isNumber(makerTokenAmount) ? new BigNumber(makerTokenAmount) : makerTokenAmount,
|
||||
takerTokenAmount: _.isNumber(takerTokenAmount) ? new BigNumber(takerTokenAmount) : takerTokenAmount,
|
||||
makerFee,
|
||||
takerFee,
|
||||
makerTokenAmount,
|
||||
takerTokenAmount,
|
||||
makerTokenAddress,
|
||||
takerTokenAddress,
|
||||
salt: ZeroEx.generatePseudoRandomSalt(),
|
||||
feeRecipient: constants.NULL_ADDRESS,
|
||||
feeRecipient,
|
||||
expirationUnixTimestampSec,
|
||||
};
|
||||
const orderHash = await zeroEx.getOrderHashHexAsync(order);
|
||||
|
24
test/utils/token_utils.ts
Normal file
24
test/utils/token_utils.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import * as _ from 'lodash';
|
||||
import {Token, ZeroExError} from '../../src/types';
|
||||
|
||||
const PROTOCOL_TOKEN_SYMBOL = 'ZRX';
|
||||
|
||||
export class TokenUtils {
|
||||
private tokens: Token[];
|
||||
constructor(tokens: Token[]) {
|
||||
this.tokens = tokens;
|
||||
}
|
||||
public getProtocolTokenOrThrow(): Token {
|
||||
const zrxToken = _.find(this.tokens, {symbol: PROTOCOL_TOKEN_SYMBOL});
|
||||
if (_.isUndefined(zrxToken)) {
|
||||
throw new Error(ZeroExError.ZRX_NOT_IN_TOKEN_REGISTRY);
|
||||
}
|
||||
return zrxToken;
|
||||
}
|
||||
public getNonProtocolTokens(): Token[] {
|
||||
const nonProtocolTokens = _.filter(this.tokens, token => {
|
||||
return token.symbol !== PROTOCOL_TOKEN_SYMBOL;
|
||||
});
|
||||
return nonProtocolTokens;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user