import './env/environment'; import {web3ProviderFactory} from './lib/web3-provider-wrapper'; import { ZeroEx, SubscriptionOpts, BlockParam, IndexedFilterValues, ExchangeEvents, ContractEvent, SignedOrder, Token, ExchangeContractErrs, ZeroExError } from '0x.js'; import * as mocha from 'mocha'; import * as chai from 'chai'; import * as Web3 from 'web3'; import BigNumber from 'bignumber.js'; import {FillScenarios} from './lib/fill_scenarios'; import {BlockchainLifecycle} from './lib/blockchain_lifecycle'; import {TokenUtils} from './lib/token_utils'; import ChaiBigNumber = require('chai-bignumber'); import chaiAsPromised = require('chai-as-promised'); import * as dirtyChai from 'dirty-chai'; import {OrderValidation} from '../src/order-validation'; chai.config.includeStack = true; chai.use(ChaiBigNumber()); // chai.use(dirtyChai); chai.use(chaiAsPromised); const expect = chai.expect; const blockchainLifecycle = new BlockchainLifecycle(); /* tslint:disable:no-unused-expression */ describe('OrderValidation', () => { let zeroEx: ZeroEx; let userAddresses: string[]; let tokens: Token[]; let tokenUtils: TokenUtils; let exchangeContractAddress: string; let zrxTokenAddress: string; let fillScenarios: FillScenarios; let makerTokenAddress: string; let takerTokenAddress: string; let coinbase: string; let makerAddress: string; let takerAddress: string; let feeRecipient: string; const fillableAmount = new BigNumber(5); const fillTakerAmount = new BigNumber(5); const shouldThrowOnInsufficientBalanceOrAllowance = false; before(async () => { const web3provider = web3ProviderFactory.getRpcProvider( process.env.RPC_HTTP_SCHEME, process.env.RPC_HOST, process.env.RPC_PORT); zeroEx = new ZeroEx(web3provider); exchangeContractAddress = await zeroEx.exchange.getContractAddressAsync(); userAddresses = await zeroEx.getAvailableAddressesAsync(); [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses; tokens = await zeroEx.tokenRegistry.getTokensAsync(); tokenUtils = new TokenUtils(tokens); zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address; fillScenarios = new FillScenarios(zeroEx, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress); const [makerToken, takerToken] = tokenUtils.getNonProtocolTokens(); makerTokenAddress = makerToken.address; takerTokenAddress = takerToken.address; }); beforeEach(async () => { await blockchainLifecycle.startAsync(); }); afterEach(async () => { await blockchainLifecycle.revertAsync(); }); describe('0x.js validator', () => { it('should throw when the fill amount is zero', async () => { const signedOrder = await fillScenarios.createFillableSignedOrderAsync( makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, ); const zeroFillAmount = new BigNumber(0); return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync( signedOrder, zeroFillAmount, takerAddress, )).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); }); // This test is not behaving properly // it('should throw when the signature is invalid', async () => { // const signedOrder = await fillScenarios.createFillableSignedOrderAsync( // makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, // ); // // 27 <--> 28 // signedOrder.ecSignature.v = 27 + (28 - signedOrder.ecSignature.v); // return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync( // signedOrder, fillableAmount, takerAddress, // )).to.be.rejectedWith(ZeroExError.InvalidSignature); // }); it('should throw when the order is fully filled or cancelled', async () => { const signedOrder = await fillScenarios.createFillableSignedOrderAsync( makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, ); await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount); return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync( signedOrder, fillableAmount, takerAddress, )).to.be.rejectedWith(ExchangeContractErrs.OrderRemainingFillAmountZero); }); it('should throw when sender is not a taker', async () => { const signedOrder = await fillScenarios.createFillableSignedOrderAsync( makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, ); const nonTakerAddress = userAddresses[6]; return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync( signedOrder, fillTakerAmount, nonTakerAddress, )).to.be.rejectedWith(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker); }); it('should throw when order is expired', async () => { const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017 const signedOrder = await fillScenarios.createFillableSignedOrderAsync( makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, expirationInPast, ); return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync( signedOrder, fillTakerAmount, takerAddress, )).to.be.rejectedWith(ExchangeContractErrs.OrderFillExpired); }); 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); return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync( signedOrder, fillTakerAmountThatCausesRoundingError, takerAddress, )).to.be.rejectedWith(ExchangeContractErrs.OrderFillRoundingError); }); }); describe('0x-relay-utils validator', () => { // it('should throw when the fill amount is zero', async () => { // const signedOrder = await fillScenarios.createFillableSignedOrderAsync( // makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, // ); // const zeroFillAmount = new BigNumber(0); // return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync( // signedOrder, zeroFillAmount, takerAddress, // )).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); // }); // it('should throw when the signature is invalid', async () => { // const signedOrder = await fillScenarios.createFillableSignedOrderAsync( // makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, // ); // // 27 <--> 28 // signedOrder.ecSignature.v = 27 + (28 - signedOrder.ecSignature.v); // return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync( // signedOrder, fillableAmount, takerAddress, // )).to.be.rejectedWith(ZeroExError.InvalidSignature); // }); // it('should throw when the order is fully filled or cancelled', async () => { // const signedOrder = await fillScenarios.createFillableSignedOrderAsync( // makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, // ); // await zeroEx.exchange.cancelOrderAsync(signedOrder, fillableAmount); // return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync( // signedOrder, fillableAmount, takerAddress, // )).to.be.rejectedWith(ExchangeContractErrs.OrderRemainingFillAmountZero); // }); // it('should throw when sender is not a taker', async () => { // const signedOrder = await fillScenarios.createFillableSignedOrderAsync( // makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, // ); // const nonTakerAddress = userAddresses[6]; // return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync( // signedOrder, fillTakerAmount, nonTakerAddress, // )).to.be.rejectedWith(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker); // }); it('should return false when order is expired', async () => { const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017 const signedOrder = await fillScenarios.createFillableSignedOrderAsync( makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, expirationInPast, ); const result: boolean = await OrderValidation.isOrderValidSimple(signedOrder); expect(result).to.equal(false); }); }); });