From 6cc5e45183ca2ae6d4dd2176ecb5efdce53f38ea Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Wed, 27 Jun 2018 11:19:10 +0300 Subject: Remove exchange-related functionality for now --- .../src/contract_wrappers/exchange_wrapper.ts | 988 ---------------- .../contract_wrappers/token_registry_wrapper.ts | 133 --- .../simple_balance_and_proxy_allowance_fetcher.ts | 28 - .../simple_order_filled_cancelled_fetcher.ts | 36 - .../stores/balance_proxy_allowance_lazy_store.ts | 91 -- .../stores/order_filled_cancelled_lazy_store.ts | 75 -- .../src/utils/exchange_transfer_simulator.ts | 112 -- .../src/utils/order_validation_utils.ts | 202 ---- .../test/exchange_transfer_simulator_test.ts | 124 -- .../test/exchange_wrapper_test.ts | 1227 -------------------- .../test/order_validation_test.ts | 539 --------- .../test/token_registry_wrapper_test.ts | 136 --- 12 files changed, 3691 deletions(-) delete mode 100644 packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts delete mode 100644 packages/contract-wrappers/src/contract_wrappers/token_registry_wrapper.ts delete mode 100644 packages/contract-wrappers/src/fetchers/simple_balance_and_proxy_allowance_fetcher.ts delete mode 100644 packages/contract-wrappers/src/fetchers/simple_order_filled_cancelled_fetcher.ts delete mode 100644 packages/contract-wrappers/src/stores/balance_proxy_allowance_lazy_store.ts delete mode 100644 packages/contract-wrappers/src/stores/order_filled_cancelled_lazy_store.ts delete mode 100644 packages/contract-wrappers/src/utils/exchange_transfer_simulator.ts delete mode 100644 packages/contract-wrappers/src/utils/order_validation_utils.ts delete mode 100644 packages/contract-wrappers/test/exchange_transfer_simulator_test.ts delete mode 100644 packages/contract-wrappers/test/exchange_wrapper_test.ts delete mode 100644 packages/contract-wrappers/test/order_validation_test.ts delete mode 100644 packages/contract-wrappers/test/token_registry_wrapper_test.ts diff --git a/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts deleted file mode 100644 index 8548a06b6..000000000 --- a/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts +++ /dev/null @@ -1,988 +0,0 @@ -import { schemas } from '@0xproject/json-schemas'; -import { formatters, getOrderHashHex, OrderStateUtils } from '@0xproject/order-utils'; -import { - BlockParamLiteral, - ContractAbi, - DecodedLogArgs, - ECSignature, - ExchangeContractErrs, - LogEntry, - LogWithDecodedArgs, - Order, - OrderState, - SignedOrder, -} from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; -import { Web3Wrapper } from '@0xproject/web3-wrapper'; -import * as _ from 'lodash'; - -import { artifacts } from '../artifacts'; -import { SimpleBalanceAndProxyAllowanceFetcher } from '../fetchers/simple_balance_and_proxy_allowance_fetcher'; -import { SimpleOrderFilledCancelledFetcher } from '../fetchers/simple_order_filled_cancelled_fetcher'; -import { BalanceAndProxyAllowanceLazyStore } from '../stores/balance_proxy_allowance_lazy_store'; -import { - BlockRange, - EventCallback, - ExchangeContractErrCodes, - IndexedFilterValues, - MethodOpts, - OrderCancellationRequest, - OrderFillRequest, - OrderTransactionOpts, - ValidateOrderFillableOpts, -} from '../types'; -import { assert } from '../utils/assert'; -import { decorators } from '../utils/decorators'; -import { ExchangeTransferSimulator } from '../utils/exchange_transfer_simulator'; -import { OrderValidationUtils } from '../utils/order_validation_utils'; - -import { ContractWrapper } from './contract_wrapper'; -import { - ExchangeContract, - ExchangeContractEventArgs, - ExchangeEvents, - LogErrorContractEventArgs, -} from './generated/exchange'; -import { TokenWrapper } from './token_wrapper'; -const SHOULD_VALIDATE_BY_DEFAULT = true; - -interface ExchangeContractErrCodesToMsgs { - [exchangeContractErrCodes: number]: string; -} - -/** - * This class includes all the functionality related to calling methods and subscribing to - * events of the 0x Exchange smart contract. - */ -export class ExchangeWrapper extends ContractWrapper { - public abi: ContractAbi = artifacts.Exchange.abi; - private _exchangeContractIfExists?: ExchangeContract; - private _orderValidationUtilsIfExists?: OrderValidationUtils; - private _tokenWrapper: TokenWrapper; - private _exchangeContractErrCodesToMsg: ExchangeContractErrCodesToMsgs = { - [ExchangeContractErrCodes.ERROR_FILL_EXPIRED]: ExchangeContractErrs.OrderFillExpired, - [ExchangeContractErrCodes.ERROR_CANCEL_EXPIRED]: ExchangeContractErrs.OrderFillExpired, - [ExchangeContractErrCodes.ERROR_FILL_NO_VALUE]: ExchangeContractErrs.OrderRemainingFillAmountZero, - [ExchangeContractErrCodes.ERROR_CANCEL_NO_VALUE]: ExchangeContractErrs.OrderRemainingFillAmountZero, - [ExchangeContractErrCodes.ERROR_FILL_TRUNCATION]: ExchangeContractErrs.OrderFillRoundingError, - [ExchangeContractErrCodes.ERROR_FILL_BALANCE_ALLOWANCE]: ExchangeContractErrs.FillBalanceAllowanceError, - }; - private _contractAddressIfExists?: string; - private _zrxContractAddressIfExists?: string; - constructor( - web3Wrapper: Web3Wrapper, - networkId: number, - tokenWrapper: TokenWrapper, - contractAddressIfExists?: string, - zrxContractAddressIfExists?: string, - ) { - super(web3Wrapper, networkId); - this._tokenWrapper = tokenWrapper; - this._contractAddressIfExists = contractAddressIfExists; - this._zrxContractAddressIfExists = zrxContractAddressIfExists; - } - /** - * Returns the unavailable takerAmount of an order. Unavailable amount is defined as the total - * amount that has been filled or cancelled. The remaining takerAmount can be calculated by - * subtracting the unavailable amount from the total order takerAmount. - * @param orderHash The hex encoded orderHash for which you would like to retrieve the - * unavailable takerAmount. - * @param methodOpts Optional arguments this method accepts. - * @return The amount of the order (in taker tokens) that has either been filled or cancelled. - */ - public async getUnavailableTakerAmountAsync(orderHash: string, methodOpts?: MethodOpts): Promise { - assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema); - - const exchangeContract = await this._getExchangeContractAsync(); - const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock; - const txData = {}; - let unavailableTakerTokenAmount = await exchangeContract.getUnavailableTakerTokenAmount.callAsync( - orderHash, - txData, - defaultBlock, - ); - // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber - unavailableTakerTokenAmount = new BigNumber(unavailableTakerTokenAmount); - return unavailableTakerTokenAmount; - } - /** - * Retrieve the takerAmount of an order that has already been filled. - * @param orderHash The hex encoded orderHash for which you would like to retrieve the filled takerAmount. - * @param methodOpts Optional arguments this method accepts. - * @return The amount of the order (in taker tokens) that has already been filled. - */ - public async getFilledTakerAmountAsync(orderHash: string, methodOpts?: MethodOpts): Promise { - assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema); - - const exchangeContract = await this._getExchangeContractAsync(); - const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock; - const txData = {}; - let fillAmountInBaseUnits = await exchangeContract.filled.callAsync(orderHash, txData, defaultBlock); - // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber - fillAmountInBaseUnits = new BigNumber(fillAmountInBaseUnits); - return fillAmountInBaseUnits; - } - /** - * Retrieve the takerAmount of an order that has been cancelled. - * @param orderHash The hex encoded orderHash for which you would like to retrieve the - * cancelled takerAmount. - * @param methodOpts Optional arguments this method accepts. - * @return The amount of the order (in taker tokens) that has been cancelled. - */ - public async getCancelledTakerAmountAsync(orderHash: string, methodOpts?: MethodOpts): Promise { - assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema); - - const exchangeContract = await this._getExchangeContractAsync(); - const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock; - const txData = {}; - let cancelledAmountInBaseUnits = await exchangeContract.cancelled.callAsync(orderHash, txData, defaultBlock); - // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber - cancelledAmountInBaseUnits = new BigNumber(cancelledAmountInBaseUnits); - return cancelledAmountInBaseUnits; - } - /** - * Fills a signed order with an amount 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 `shouldThrowOnInsufficientBalanceOrAllowance`. - * If false, the smart contract will not throw if the parties - * do not have sufficient balances/allowances, preserving gas costs. Setting it to true forgoes this check - * and causes the smart contract to throw (using all the gas supplied) instead. - * @param signedOrder An object that conforms to the SignedOrder interface. - * @param fillTakerTokenAmount The amount of the order (in taker tokens baseUnits) that - * you wish to fill. - * @param shouldThrowOnInsufficientBalanceOrAllowance Whether or not you wish for the contract call to throw - * if upon execution the tokens cannot be transferred. - * @param takerAddress The user Ethereum address who would like to fill this order. - * Must be available via the supplied Provider - * passed to 0x.js. - * @param orderTransactionOpts Optional arguments this method accepts. - * @return Transaction hash. - */ - @decorators.asyncZeroExErrorHandler - public async fillOrderAsync( - signedOrder: SignedOrder, - fillTakerTokenAmount: BigNumber, - shouldThrowOnInsufficientBalanceOrAllowance: boolean, - takerAddress: string, - orderTransactionOpts: OrderTransactionOpts = {}, - ): Promise { - assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); - assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount); - assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance); - await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); - const normalizedTakerAddress = takerAddress.toLowerCase(); - - const exchangeInstance = await this._getExchangeContractAsync(); - const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) - ? SHOULD_VALIDATE_BY_DEFAULT - : orderTransactionOpts.shouldValidate; - if (shouldValidate) { - const zrxTokenAddress = this.getZRXTokenAddress(); - const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( - this._tokenWrapper, - BlockParamLiteral.Latest, - ); - const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore); - const orderValidationUtils = await this._getOrderValidationUtilsAsync(); - await orderValidationUtils.validateFillOrderThrowIfInvalidAsync( - exchangeTradeEmulator, - signedOrder, - fillTakerTokenAmount, - normalizedTakerAddress, - zrxTokenAddress, - ); - } - - const [orderAddresses, orderValues] = formatters.getOrderAddressesAndValues(signedOrder); - - const txHash: string = await exchangeInstance.fillOrder.sendTransactionAsync( - orderAddresses, - orderValues, - fillTakerTokenAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - signedOrder.ecSignature.v, - signedOrder.ecSignature.r, - signedOrder.ecSignature.s, - { - from: normalizedTakerAddress, - gas: orderTransactionOpts.gasLimit, - gasPrice: orderTransactionOpts.gasPrice, - }, - ); - return txHash; - } - /** - * Sequentially and atomically fills signedOrders up to the specified takerTokenFillAmount. - * If the fill amount is reached - it succeeds and does not fill the rest of the orders. - * If fill amount is not reached - it fills as much of the fill amount as possible and succeeds. - * @param signedOrders The array of signedOrders that you would like to fill until - * takerTokenFillAmount is reached. - * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill. - * @param shouldThrowOnInsufficientBalanceOrAllowance Whether or not you wish for the contract call to throw if - * upon execution any of the tokens cannot be transferred. - * If set to false, the call will continue to fill subsequent - * signedOrders even when some cannot be filled. - * @param takerAddress The user Ethereum address who would like to fill these - * orders. Must be available via the supplied Provider - * passed to 0x.js. - * @param orderTransactionOpts Optional arguments this method accepts. - * @return Transaction hash. - */ - @decorators.asyncZeroExErrorHandler - public async fillOrdersUpToAsync( - signedOrders: SignedOrder[], - fillTakerTokenAmount: BigNumber, - shouldThrowOnInsufficientBalanceOrAllowance: boolean, - takerAddress: string, - orderTransactionOpts: OrderTransactionOpts = {}, - ): Promise { - assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); - const takerTokenAddresses = _.map(signedOrders, signedOrder => signedOrder.takerTokenAddress); - assert.hasAtMostOneUniqueValue( - takerTokenAddresses, - ExchangeContractErrs.MultipleTakerTokensInFillUpToDisallowed, - ); - const exchangeContractAddresses = _.map(signedOrders, signedOrder => signedOrder.exchangeContractAddress); - assert.hasAtMostOneUniqueValue( - exchangeContractAddresses, - ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress, - ); - assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount); - assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance); - await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); - const normalizedTakerAddress = takerAddress.toLowerCase(); - - const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) - ? SHOULD_VALIDATE_BY_DEFAULT - : orderTransactionOpts.shouldValidate; - if (shouldValidate) { - let filledTakerTokenAmount = new BigNumber(0); - const zrxTokenAddress = this.getZRXTokenAddress(); - const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( - this._tokenWrapper, - BlockParamLiteral.Latest, - ); - const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore); - const orderValidationUtils = await this._getOrderValidationUtilsAsync(); - for (const signedOrder of signedOrders) { - const singleFilledTakerTokenAmount = await orderValidationUtils.validateFillOrderThrowIfInvalidAsync( - exchangeTradeEmulator, - signedOrder, - fillTakerTokenAmount.minus(filledTakerTokenAmount), - normalizedTakerAddress, - zrxTokenAddress, - ); - filledTakerTokenAmount = filledTakerTokenAmount.plus(singleFilledTakerTokenAmount); - if (filledTakerTokenAmount.eq(fillTakerTokenAmount)) { - break; - } - } - } - - if (_.isEmpty(signedOrders)) { - throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem); - } - - const orderAddressesValuesAndSignatureArray = _.map(signedOrders, signedOrder => { - return [ - ...formatters.getOrderAddressesAndValues(signedOrder), - signedOrder.ecSignature.v, - signedOrder.ecSignature.r, - signedOrder.ecSignature.s, - ]; - }); - // We use _.unzip because _.unzip doesn't type check if values have different types :'( - const [orderAddressesArray, orderValuesArray, vArray, rArray, sArray] = _.unzip( - orderAddressesValuesAndSignatureArray, - ); - - const exchangeInstance = await this._getExchangeContractAsync(); - const txHash = await exchangeInstance.fillOrdersUpTo.sendTransactionAsync( - orderAddressesArray, - orderValuesArray, - fillTakerTokenAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - vArray, - rArray, - sArray, - { - from: normalizedTakerAddress, - gas: orderTransactionOpts.gasLimit, - gasPrice: orderTransactionOpts.gasPrice, - }, - ); - return txHash; - } - /** - * Batch version of fillOrderAsync. - * Executes multiple fills atomically in a single transaction. - * If shouldThrowOnInsufficientBalanceOrAllowance is set to false, it will continue filling subsequent orders even - * when earlier ones fail. - * When shouldThrowOnInsufficientBalanceOrAllowance is set to true, if any fill fails, the entire batch fails. - * @param orderFillRequests An array of objects that conform to the - * OrderFillRequest interface. - * @param shouldThrowOnInsufficientBalanceOrAllowance Whether or not you wish for the contract call to throw - * if upon execution any of the tokens cannot be - * transferred. If set to false, the call will continue to - * fill subsequent signedOrders even when some - * cannot be filled. - * @param takerAddress The user Ethereum address who would like to fill - * these orders. Must be available via the supplied - * Provider passed to 0x.js. - * @param orderTransactionOpts Optional arguments this method accepts. - * @return Transaction hash. - */ - @decorators.asyncZeroExErrorHandler - public async batchFillOrdersAsync( - orderFillRequests: OrderFillRequest[], - shouldThrowOnInsufficientBalanceOrAllowance: boolean, - takerAddress: string, - orderTransactionOpts: OrderTransactionOpts = {}, - ): Promise { - assert.doesConformToSchema('orderFillRequests', orderFillRequests, schemas.orderFillRequestsSchema); - const exchangeContractAddresses = _.map( - orderFillRequests, - orderFillRequest => orderFillRequest.signedOrder.exchangeContractAddress, - ); - assert.hasAtMostOneUniqueValue( - exchangeContractAddresses, - ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress, - ); - assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance); - await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); - const normalizedTakerAddress = takerAddress.toLowerCase(); - const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) - ? SHOULD_VALIDATE_BY_DEFAULT - : orderTransactionOpts.shouldValidate; - if (shouldValidate) { - const zrxTokenAddress = this.getZRXTokenAddress(); - const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( - this._tokenWrapper, - BlockParamLiteral.Latest, - ); - const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore); - const orderValidationUtils = await this._getOrderValidationUtilsAsync(); - for (const orderFillRequest of orderFillRequests) { - await orderValidationUtils.validateFillOrderThrowIfInvalidAsync( - exchangeTradeEmulator, - orderFillRequest.signedOrder, - orderFillRequest.takerTokenFillAmount, - normalizedTakerAddress, - zrxTokenAddress, - ); - } - } - if (_.isEmpty(orderFillRequests)) { - throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem); - } - - const orderAddressesValuesAmountsAndSignatureArray = _.map(orderFillRequests, orderFillRequest => { - return [ - ...formatters.getOrderAddressesAndValues(orderFillRequest.signedOrder), - orderFillRequest.takerTokenFillAmount, - orderFillRequest.signedOrder.ecSignature.v, - orderFillRequest.signedOrder.ecSignature.r, - orderFillRequest.signedOrder.ecSignature.s, - ]; - }); - // We use _.unzip because _.unzip doesn't type check if values have different types :'( - const [orderAddressesArray, orderValuesArray, fillTakerTokenAmounts, vArray, rArray, sArray] = _.unzip( - orderAddressesValuesAmountsAndSignatureArray, - ); - - const exchangeInstance = await this._getExchangeContractAsync(); - const txHash = await exchangeInstance.batchFillOrders.sendTransactionAsync( - orderAddressesArray, - orderValuesArray, - fillTakerTokenAmounts, - shouldThrowOnInsufficientBalanceOrAllowance, - vArray, - rArray, - sArray, - { - from: normalizedTakerAddress, - gas: orderTransactionOpts.gasLimit, - gasPrice: orderTransactionOpts.gasPrice, - }, - ); - return txHash; - } - /** - * Attempts to fill a specific amount of an order. If the entire amount specified cannot be filled, - * the fill order is abandoned. - * @param signedOrder An object that conforms to the SignedOrder interface. The - * signedOrder you wish to fill. - * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill. - * @param takerAddress The user Ethereum address who would like to fill this order. - * Must be available via the supplied Provider passed to 0x.js. - * @param orderTransactionOpts Optional arguments this method accepts. - * @return Transaction hash. - */ - @decorators.asyncZeroExErrorHandler - public async fillOrKillOrderAsync( - signedOrder: SignedOrder, - fillTakerTokenAmount: BigNumber, - takerAddress: string, - orderTransactionOpts: OrderTransactionOpts = {}, - ): Promise { - assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); - assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount); - await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); - const normalizedTakerAddress = takerAddress.toLowerCase(); - - const exchangeInstance = await this._getExchangeContractAsync(); - - const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) - ? SHOULD_VALIDATE_BY_DEFAULT - : orderTransactionOpts.shouldValidate; - if (shouldValidate) { - const zrxTokenAddress = this.getZRXTokenAddress(); - const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( - this._tokenWrapper, - BlockParamLiteral.Latest, - ); - const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore); - const orderValidationUtils = await this._getOrderValidationUtilsAsync(); - await orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync( - exchangeTradeEmulator, - signedOrder, - fillTakerTokenAmount, - normalizedTakerAddress, - zrxTokenAddress, - ); - } - - const [orderAddresses, orderValues] = formatters.getOrderAddressesAndValues(signedOrder); - const txHash = await exchangeInstance.fillOrKillOrder.sendTransactionAsync( - orderAddresses, - orderValues, - fillTakerTokenAmount, - signedOrder.ecSignature.v, - signedOrder.ecSignature.r, - signedOrder.ecSignature.s, - { - from: normalizedTakerAddress, - gas: orderTransactionOpts.gasLimit, - gasPrice: orderTransactionOpts.gasPrice, - }, - ); - return txHash; - } - /** - * Batch version of fillOrKill. Allows a taker to specify a batch of orders that will either be atomically - * filled (each to the specified fillAmount) or aborted. - * @param orderFillRequests An array of objects that conform to the OrderFillRequest interface. - * @param takerAddress The user Ethereum address who would like to fill there orders. - * Must be available via the supplied Provider passed to 0x.js. - * @param orderTransactionOpts Optional arguments this method accepts. - * @return Transaction hash. - */ - @decorators.asyncZeroExErrorHandler - public async batchFillOrKillAsync( - orderFillRequests: OrderFillRequest[], - takerAddress: string, - orderTransactionOpts: OrderTransactionOpts = {}, - ): Promise { - assert.doesConformToSchema('orderFillRequests', orderFillRequests, schemas.orderFillRequestsSchema); - const exchangeContractAddresses = _.map( - orderFillRequests, - orderFillRequest => orderFillRequest.signedOrder.exchangeContractAddress, - ); - assert.hasAtMostOneUniqueValue( - exchangeContractAddresses, - ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress, - ); - await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); - const normalizedTakerAddress = takerAddress.toLowerCase(); - if (_.isEmpty(orderFillRequests)) { - throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem); - } - const exchangeInstance = await this._getExchangeContractAsync(); - - const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) - ? SHOULD_VALIDATE_BY_DEFAULT - : orderTransactionOpts.shouldValidate; - if (shouldValidate) { - const zrxTokenAddress = this.getZRXTokenAddress(); - const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( - this._tokenWrapper, - BlockParamLiteral.Latest, - ); - const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore); - const orderValidationUtils = await this._getOrderValidationUtilsAsync(); - for (const orderFillRequest of orderFillRequests) { - await orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync( - exchangeTradeEmulator, - orderFillRequest.signedOrder, - orderFillRequest.takerTokenFillAmount, - normalizedTakerAddress, - zrxTokenAddress, - ); - } - } - - const orderAddressesValuesAndTakerTokenFillAmounts = _.map(orderFillRequests, request => { - return [ - ...formatters.getOrderAddressesAndValues(request.signedOrder), - request.takerTokenFillAmount, - request.signedOrder.ecSignature.v, - request.signedOrder.ecSignature.r, - request.signedOrder.ecSignature.s, - ]; - }); - - // We use _.unzip because _.unzip doesn't type check if values have different types :'( - const [orderAddresses, orderValues, fillTakerTokenAmounts, vParams, rParams, sParams] = _.unzip( - orderAddressesValuesAndTakerTokenFillAmounts, - ); - const txHash = await exchangeInstance.batchFillOrKillOrders.sendTransactionAsync( - orderAddresses, - orderValues, - fillTakerTokenAmounts, - vParams, - rParams, - sParams, - { - from: normalizedTakerAddress, - gas: orderTransactionOpts.gasLimit, - gasPrice: orderTransactionOpts.gasPrice, - }, - ); - return txHash; - } - /** - * Cancel a given fill amount of an order. Cancellations are cumulative. - * @param order An object that conforms to the Order or SignedOrder interface. - * The order you would like to cancel. - * @param cancelTakerTokenAmount The amount (specified in taker tokens) that you would like to cancel. - * @param transactionOpts Optional arguments this method accepts. - * @return Transaction hash. - */ - @decorators.asyncZeroExErrorHandler - public async cancelOrderAsync( - order: Order | SignedOrder, - cancelTakerTokenAmount: BigNumber, - orderTransactionOpts: OrderTransactionOpts = {}, - ): Promise { - assert.doesConformToSchema('order', order, schemas.orderSchema); - assert.isValidBaseUnitAmount('takerTokenCancelAmount', cancelTakerTokenAmount); - await assert.isSenderAddressAsync('order.maker', order.maker, this._web3Wrapper); - const normalizedMakerAddress = order.maker.toLowerCase(); - - const exchangeInstance = await this._getExchangeContractAsync(); - - const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) - ? SHOULD_VALIDATE_BY_DEFAULT - : orderTransactionOpts.shouldValidate; - if (shouldValidate) { - const orderHash = getOrderHashHex(order); - const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash); - OrderValidationUtils.validateCancelOrderThrowIfInvalid( - order, - cancelTakerTokenAmount, - unavailableTakerTokenAmount, - ); - } - - const [orderAddresses, orderValues] = formatters.getOrderAddressesAndValues(order); - const txHash = await exchangeInstance.cancelOrder.sendTransactionAsync( - orderAddresses, - orderValues, - cancelTakerTokenAmount, - { - from: normalizedMakerAddress, - gas: orderTransactionOpts.gasLimit, - gasPrice: orderTransactionOpts.gasPrice, - }, - ); - return txHash; - } - /** - * Batch version of cancelOrderAsync. Atomically cancels multiple orders in a single transaction. - * All orders must be from the same maker. - * @param orderCancellationRequests An array of objects that conform to the OrderCancellationRequest - * interface. - * @param transactionOpts Optional arguments this method accepts. - * @return Transaction hash. - */ - @decorators.asyncZeroExErrorHandler - public async batchCancelOrdersAsync( - orderCancellationRequests: OrderCancellationRequest[], - orderTransactionOpts: OrderTransactionOpts = {}, - ): Promise { - assert.doesConformToSchema( - 'orderCancellationRequests', - orderCancellationRequests, - schemas.orderCancellationRequestsSchema, - ); - const exchangeContractAddresses = _.map( - orderCancellationRequests, - orderCancellationRequest => orderCancellationRequest.order.exchangeContractAddress, - ); - assert.hasAtMostOneUniqueValue( - exchangeContractAddresses, - ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress, - ); - const makers = _.map(orderCancellationRequests, cancellationRequest => cancellationRequest.order.maker); - assert.hasAtMostOneUniqueValue(makers, ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed); - const maker = makers[0]; - await assert.isSenderAddressAsync('maker', maker, this._web3Wrapper); - const normalizedMakerAddress = maker.toLowerCase(); - - const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) - ? SHOULD_VALIDATE_BY_DEFAULT - : orderTransactionOpts.shouldValidate; - if (shouldValidate) { - for (const orderCancellationRequest of orderCancellationRequests) { - const orderHash = getOrderHashHex(orderCancellationRequest.order); - const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash); - OrderValidationUtils.validateCancelOrderThrowIfInvalid( - orderCancellationRequest.order, - orderCancellationRequest.takerTokenCancelAmount, - unavailableTakerTokenAmount, - ); - } - } - if (_.isEmpty(orderCancellationRequests)) { - throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem); - } - const exchangeInstance = await this._getExchangeContractAsync(); - const orderAddressesValuesAndTakerTokenCancelAmounts = _.map(orderCancellationRequests, cancellationRequest => { - return [ - ...formatters.getOrderAddressesAndValues(cancellationRequest.order), - cancellationRequest.takerTokenCancelAmount, - ]; - }); - // We use _.unzip because _.unzip doesn't type check if values have different types :'( - const [orderAddresses, orderValues, cancelTakerTokenAmounts] = _.unzip( - orderAddressesValuesAndTakerTokenCancelAmounts, - ); - const txHash = await exchangeInstance.batchCancelOrders.sendTransactionAsync( - orderAddresses, - orderValues, - cancelTakerTokenAmounts, - { - from: normalizedMakerAddress, - gas: orderTransactionOpts.gasLimit, - gasPrice: orderTransactionOpts.gasPrice, - }, - ); - return txHash; - } - /** - * Subscribe to an event type emitted by the Exchange contract. - * @param eventName The exchange contract event you would like to subscribe to. - * @param indexFilterValues An object where the keys are indexed args returned by the event and - * the value is the value you are interested in. E.g `{maker: aUserAddressHex}` - * @param callback Callback that gets called when a log is added/removed - * @return Subscription token used later to unsubscribe - */ - public subscribe( - eventName: ExchangeEvents, - indexFilterValues: IndexedFilterValues, - callback: EventCallback, - ): string { - assert.doesBelongToStringEnum('eventName', eventName, ExchangeEvents); - assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema); - assert.isFunction('callback', callback); - const exchangeContractAddress = this.getContractAddress(); - const subscriptionToken = this._subscribe( - exchangeContractAddress, - eventName, - indexFilterValues, - artifacts.Exchange.abi, - callback, - ); - return subscriptionToken; - } - /** - * Cancel a subscription - * @param subscriptionToken Subscription token returned by `subscribe()` - */ - public unsubscribe(subscriptionToken: string): void { - this._unsubscribe(subscriptionToken); - } - /** - * Cancels all existing subscriptions - */ - public unsubscribeAll(): void { - super._unsubscribeAll(); - } - /** - * Gets historical logs without creating a subscription - * @param eventName The exchange contract event you would like to subscribe to. - * @param blockRange Block range to get logs from. - * @param indexFilterValues An object where the keys are indexed args returned by the event and - * the value is the value you are interested in. E.g `{_from: aUserAddressHex}` - * @return Array of logs that match the parameters - */ - public async getLogsAsync( - eventName: ExchangeEvents, - blockRange: BlockRange, - indexFilterValues: IndexedFilterValues, - ): Promise>> { - assert.doesBelongToStringEnum('eventName', eventName, ExchangeEvents); - assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema); - assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema); - const exchangeContractAddress = this.getContractAddress(); - const logs = await this._getLogsAsync( - exchangeContractAddress, - eventName, - blockRange, - indexFilterValues, - artifacts.Exchange.abi, - ); - return logs; - } - /** - * Retrieves the Ethereum address of the Exchange contract deployed on the network - * that the user-passed web3 provider is connected to. - * @returns The Ethereum address of the Exchange contract being used. - */ - public getContractAddress(): string { - const contractAddress = this._getContractAddress(artifacts.Exchange, this._contractAddressIfExists); - return contractAddress; - } - /** - * Checks if order is still fillable and throws an error otherwise. Useful for orderbook - * pruning where you want to remove stale orders without knowing who the taker will be. - * @param signedOrder An object that conforms to the SignedOrder interface. The - * signedOrder you wish to validate. - * @param opts An object that conforms to the ValidateOrderFillableOpts - * interface. Allows specifying a specific fillTakerTokenAmount - * to validate for. - */ - public async validateOrderFillableOrThrowAsync( - signedOrder: SignedOrder, - opts?: ValidateOrderFillableOpts, - ): Promise { - assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); - const zrxTokenAddress = this.getZRXTokenAddress(); - const expectedFillTakerTokenAmount = !_.isUndefined(opts) ? opts.expectedFillTakerTokenAmount : undefined; - const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( - this._tokenWrapper, - BlockParamLiteral.Latest, - ); - const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore); - const orderValidationUtils = await this._getOrderValidationUtilsAsync(); - await orderValidationUtils.validateOrderFillableOrThrowAsync( - exchangeTradeEmulator, - signedOrder, - zrxTokenAddress, - expectedFillTakerTokenAmount, - ); - } - /** - * Checks if order fill will succeed and throws an error otherwise. - * @param signedOrder An object that conforms to the SignedOrder interface. The - * signedOrder you wish to fill. - * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill. - * @param takerAddress The user Ethereum address who would like to fill this order. - * Must be available via the supplied Provider passed to 0x.js. - */ - public async validateFillOrderThrowIfInvalidAsync( - signedOrder: SignedOrder, - fillTakerTokenAmount: BigNumber, - takerAddress: string, - ): Promise { - assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); - assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount); - await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); - const normalizedTakerAddress = takerAddress.toLowerCase(); - const zrxTokenAddress = this.getZRXTokenAddress(); - const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( - this._tokenWrapper, - BlockParamLiteral.Latest, - ); - const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore); - const orderValidationUtils = await this._getOrderValidationUtilsAsync(); - await orderValidationUtils.validateFillOrderThrowIfInvalidAsync( - exchangeTradeEmulator, - signedOrder, - fillTakerTokenAmount, - normalizedTakerAddress, - zrxTokenAddress, - ); - } - /** - * Checks if cancelling a given order will succeed and throws an informative error if it won't. - * @param order An object that conforms to the Order or SignedOrder interface. - * The order you would like to cancel. - * @param cancelTakerTokenAmount The amount (specified in taker tokens) that you would like to cancel. - */ - public async validateCancelOrderThrowIfInvalidAsync( - order: Order, - cancelTakerTokenAmount: BigNumber, - ): Promise { - assert.doesConformToSchema('order', order, schemas.orderSchema); - assert.isValidBaseUnitAmount('cancelTakerTokenAmount', cancelTakerTokenAmount); - const orderHash = getOrderHashHex(order); - const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash); - OrderValidationUtils.validateCancelOrderThrowIfInvalid( - order, - cancelTakerTokenAmount, - unavailableTakerTokenAmount, - ); - } - /** - * Checks if calling fillOrKill on a given order will succeed and throws an informative error if it won't. - * @param signedOrder An object that conforms to the SignedOrder interface. The - * signedOrder you wish to fill. - * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill. - * @param takerAddress The user Ethereum address who would like to fill this order. - * Must be available via the supplied Provider passed to 0x.js. - */ - public async validateFillOrKillOrderThrowIfInvalidAsync( - signedOrder: SignedOrder, - fillTakerTokenAmount: BigNumber, - takerAddress: string, - ): Promise { - assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); - assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount); - await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); - const normalizedTakerAddress = takerAddress.toLowerCase(); - const zrxTokenAddress = this.getZRXTokenAddress(); - const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( - this._tokenWrapper, - BlockParamLiteral.Latest, - ); - const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore); - const orderValidationUtils = await this._getOrderValidationUtilsAsync(); - await orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync( - exchangeTradeEmulator, - signedOrder, - fillTakerTokenAmount, - normalizedTakerAddress, - zrxTokenAddress, - ); - } - /** - * Checks if rounding error will be > 0.1% when computing makerTokenAmount by doing: - * `(fillTakerTokenAmount * makerTokenAmount) / takerTokenAmount`. - * 0x Protocol does not accept any trades that result in large rounding errors. This means that tokens with few or - * no decimals can only be filled in quantities and ratios that avoid large rounding errors. - * @param fillTakerTokenAmount The amount of the order (in taker tokens baseUnits) that you wish to fill. - * @param takerTokenAmount The order size on the taker side - * @param makerTokenAmount The order size on the maker side - */ - public async isRoundingErrorAsync( - fillTakerTokenAmount: BigNumber, - takerTokenAmount: BigNumber, - makerTokenAmount: BigNumber, - ): Promise { - assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount); - assert.isValidBaseUnitAmount('takerTokenAmount', takerTokenAmount); - assert.isValidBaseUnitAmount('makerTokenAmount', makerTokenAmount); - const exchangeInstance = await this._getExchangeContractAsync(); - const isRoundingError = await exchangeInstance.isRoundingError.callAsync( - fillTakerTokenAmount, - takerTokenAmount, - makerTokenAmount, - ); - return isRoundingError; - } - /** - * Checks if logs contain LogError, which is emitted by Exchange contract on transaction failure. - * @param logs Transaction logs as returned by `zeroEx.awaitTransactionMinedAsync` - */ - public throwLogErrorsAsErrors(logs: Array | LogEntry>): void { - const errLog = _.find(logs, { - event: ExchangeEvents.LogError, - }); - if (!_.isUndefined(errLog)) { - const logArgs = (errLog as LogWithDecodedArgs).args; - const errCode = logArgs.errorId; - const errMessage = this._exchangeContractErrCodesToMsg[errCode]; - throw new Error(errMessage); - } - } - /** - * Gets the latest OrderState of a signedOrder - * @param signedOrder The signedOrder - * @param stateLayer Optional, desired blockchain state layer (defaults to latest). - * @return OrderState of the signedOrder - */ - public async getOrderStateAsync( - signedOrder: SignedOrder, - stateLayer: BlockParamLiteral = BlockParamLiteral.Latest, - ): Promise { - const simpleBalanceAndProxyAllowanceFetcher = new SimpleBalanceAndProxyAllowanceFetcher( - this._tokenWrapper, - stateLayer, - ); - const simpleOrderFilledCancelledFetcher = new SimpleOrderFilledCancelledFetcher(this, stateLayer); - const orderStateUtils = new OrderStateUtils( - simpleBalanceAndProxyAllowanceFetcher, - simpleOrderFilledCancelledFetcher, - ); - const orderState = orderStateUtils.getOrderStateAsync(signedOrder); - return orderState; - } - /** - * Returns the ZRX token address used by the exchange contract. - * @return Address of ZRX token - */ - public getZRXTokenAddress(): string { - const contractAddress = this._getContractAddress(artifacts.ZRX, this._zrxContractAddressIfExists); - return contractAddress; - } - // tslint:disable:no-unused-variable - private _invalidateContractInstances(): void { - this.unsubscribeAll(); - delete this._exchangeContractIfExists; - } - private async _isValidSignatureUsingContractCallAsync( - dataHex: string, - ecSignature: ECSignature, - signerAddressHex: string, - ): Promise { - assert.isHexString('dataHex', dataHex); - assert.doesConformToSchema('ecSignature', ecSignature, schemas.ecSignatureSchema); - assert.isETHAddressHex('signerAddressHex', signerAddressHex); - const normalizedSignerAddress = signerAddressHex.toLowerCase(); - - const exchangeInstance = await this._getExchangeContractAsync(); - - const isValidSignature = await exchangeInstance.isValidSignature.callAsync( - normalizedSignerAddress, - dataHex, - ecSignature.v, - ecSignature.r, - ecSignature.s, - ); - return isValidSignature; - } - private async _getOrderHashHexUsingContractCallAsync(order: Order | SignedOrder): Promise { - const exchangeInstance = await this._getExchangeContractAsync(); - const [orderAddresses, orderValues] = formatters.getOrderAddressesAndValues(order); - const orderHashHex = await exchangeInstance.getOrderHash.callAsync(orderAddresses, orderValues); - return orderHashHex; - } - private async _getOrderValidationUtilsAsync(): Promise { - if (!_.isUndefined(this._orderValidationUtilsIfExists)) { - return this._orderValidationUtilsIfExists; - } - const exchangeContract = await this._getExchangeContractAsync(); - this._orderValidationUtilsIfExists = new OrderValidationUtils(exchangeContract); - return this._orderValidationUtilsIfExists; - } - // tslint:enable:no-unused-variable - private async _getExchangeContractAsync(): Promise { - if (!_.isUndefined(this._exchangeContractIfExists)) { - return this._exchangeContractIfExists; - } - const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync( - artifacts.Exchange, - this._contractAddressIfExists, - ); - const contractInstance = new ExchangeContract( - abi, - address, - this._web3Wrapper.getProvider(), - this._web3Wrapper.getContractDefaults(), - ); - this._exchangeContractIfExists = contractInstance; - return this._exchangeContractIfExists; - } -} // tslint:disable:max-file-line-count diff --git a/packages/contract-wrappers/src/contract_wrappers/token_registry_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/token_registry_wrapper.ts deleted file mode 100644 index 7b558ed69..000000000 --- a/packages/contract-wrappers/src/contract_wrappers/token_registry_wrapper.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { ContractAbi, Token } from '@0xproject/types'; -import { Web3Wrapper } from '@0xproject/web3-wrapper'; -import * as _ from 'lodash'; - -import { artifacts } from '../artifacts'; -import { TokenMetadata } from '../types'; -import { assert } from '../utils/assert'; -import { constants } from '../utils/constants'; - -import { ContractWrapper } from './contract_wrapper'; -import { TokenRegistryContract } from './generated/token_registry'; - -/** - * This class includes all the functionality related to interacting with the 0x Token Registry smart contract. - */ -export class TokenRegistryWrapper extends ContractWrapper { - public abi: ContractAbi = artifacts.TokenRegistry.abi; - private _tokenRegistryContractIfExists?: TokenRegistryContract; - private _contractAddressIfExists?: string; - private static _createTokenFromMetadata(metadata: TokenMetadata): Token | undefined { - if (metadata[0] === constants.NULL_ADDRESS) { - return undefined; - } - const token = { - address: metadata[0], - name: metadata[1], - symbol: metadata[2], - decimals: metadata[3], - }; - return token; - } - constructor(web3Wrapper: Web3Wrapper, networkId: number, contractAddressIfExists?: string) { - super(web3Wrapper, networkId); - this._contractAddressIfExists = contractAddressIfExists; - } - /** - * Retrieves all the tokens currently listed in the Token Registry smart contract - * @return An array of objects that conform to the Token interface. - */ - public async getTokensAsync(): Promise { - const addresses = await this.getTokenAddressesAsync(); - const tokenPromises: Array> = _.map(addresses, async (address: string) => - this.getTokenIfExistsAsync(address), - ); - const tokens = await Promise.all(tokenPromises); - return tokens as Token[]; - } - /** - * Retrieves all the addresses of the tokens currently listed in the Token Registry smart contract - * @return An array of token addresses. - */ - public async getTokenAddressesAsync(): Promise { - const tokenRegistryContract = await this._getTokenRegistryContractAsync(); - const addresses = await tokenRegistryContract.getTokenAddresses.callAsync(); - const lowerCaseAddresses = _.map(addresses, address => address.toLowerCase()); - return lowerCaseAddresses; - } - /** - * Retrieves a token by address currently listed in the Token Registry smart contract - * @return An object that conforms to the Token interface or undefined if token not found. - */ - public async getTokenIfExistsAsync(address: string): Promise { - assert.isETHAddressHex('address', address); - const normalizedAddress = address.toLowerCase(); - - const tokenRegistryContract = await this._getTokenRegistryContractAsync(); - const metadata = await tokenRegistryContract.getTokenMetaData.callAsync(normalizedAddress); - const token = TokenRegistryWrapper._createTokenFromMetadata(metadata); - return token; - } - public async getTokenAddressBySymbolIfExistsAsync(symbol: string): Promise { - assert.isString('symbol', symbol); - const tokenRegistryContract = await this._getTokenRegistryContractAsync(); - const addressIfExists = await tokenRegistryContract.getTokenAddressBySymbol.callAsync(symbol); - if (addressIfExists === constants.NULL_ADDRESS) { - return undefined; - } - return addressIfExists; - } - public async getTokenAddressByNameIfExistsAsync(name: string): Promise { - assert.isString('name', name); - const tokenRegistryContract = await this._getTokenRegistryContractAsync(); - const addressIfExists = await tokenRegistryContract.getTokenAddressByName.callAsync(name); - if (addressIfExists === constants.NULL_ADDRESS) { - return undefined; - } - return addressIfExists; - } - public async getTokenBySymbolIfExistsAsync(symbol: string): Promise { - assert.isString('symbol', symbol); - const tokenRegistryContract = await this._getTokenRegistryContractAsync(); - const metadata = await tokenRegistryContract.getTokenBySymbol.callAsync(symbol); - const token = TokenRegistryWrapper._createTokenFromMetadata(metadata); - return token; - } - public async getTokenByNameIfExistsAsync(name: string): Promise { - assert.isString('name', name); - const tokenRegistryContract = await this._getTokenRegistryContractAsync(); - const metadata = await tokenRegistryContract.getTokenByName.callAsync(name); - const token = TokenRegistryWrapper._createTokenFromMetadata(metadata); - return token; - } - /** - * Retrieves the Ethereum address of the TokenRegistry contract deployed on the network - * that the user-passed web3 provider is connected to. - * @returns The Ethereum address of the TokenRegistry contract being used. - */ - public getContractAddress(): string { - const contractAddress = this._getContractAddress(artifacts.TokenRegistry, this._contractAddressIfExists); - return contractAddress; - } - // tslint:disable-next-line:no-unused-variable - private _invalidateContractInstance(): void { - delete this._tokenRegistryContractIfExists; - } - private async _getTokenRegistryContractAsync(): Promise { - if (!_.isUndefined(this._tokenRegistryContractIfExists)) { - return this._tokenRegistryContractIfExists; - } - const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync( - artifacts.TokenRegistry, - this._contractAddressIfExists, - ); - const contractInstance = new TokenRegistryContract( - abi, - address, - this._web3Wrapper.getProvider(), - this._web3Wrapper.getContractDefaults(), - ); - this._tokenRegistryContractIfExists = contractInstance; - return this._tokenRegistryContractIfExists; - } -} diff --git a/packages/contract-wrappers/src/fetchers/simple_balance_and_proxy_allowance_fetcher.ts b/packages/contract-wrappers/src/fetchers/simple_balance_and_proxy_allowance_fetcher.ts deleted file mode 100644 index 5017d879a..000000000 --- a/packages/contract-wrappers/src/fetchers/simple_balance_and_proxy_allowance_fetcher.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { AbstractBalanceAndProxyAllowanceFetcher } from '@0xproject/order-utils'; -import { BlockParamLiteral } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; - -import { TokenWrapper } from '../contract_wrappers/token_wrapper'; - -export class SimpleBalanceAndProxyAllowanceFetcher implements AbstractBalanceAndProxyAllowanceFetcher { - private _tokenWrapper: TokenWrapper; - private _defaultBlock: BlockParamLiteral; - constructor(token: TokenWrapper, defaultBlock: BlockParamLiteral) { - this._tokenWrapper = token; - this._defaultBlock = defaultBlock; - } - public async getBalanceAsync(tokenAddress: string, userAddress: string): Promise { - const methodOpts = { - defaultBlock: this._defaultBlock, - }; - const balance = this._tokenWrapper.getBalanceAsync(tokenAddress, userAddress, methodOpts); - return balance; - } - public async getProxyAllowanceAsync(tokenAddress: string, userAddress: string): Promise { - const methodOpts = { - defaultBlock: this._defaultBlock, - }; - const proxyAllowance = this._tokenWrapper.getProxyAllowanceAsync(tokenAddress, userAddress, methodOpts); - return proxyAllowance; - } -} diff --git a/packages/contract-wrappers/src/fetchers/simple_order_filled_cancelled_fetcher.ts b/packages/contract-wrappers/src/fetchers/simple_order_filled_cancelled_fetcher.ts deleted file mode 100644 index 52d0a100c..000000000 --- a/packages/contract-wrappers/src/fetchers/simple_order_filled_cancelled_fetcher.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { AbstractOrderFilledCancelledFetcher } from '@0xproject/order-utils'; -import { BlockParamLiteral } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; - -import { ExchangeWrapper } from '../contract_wrappers/exchange_wrapper'; - -export class SimpleOrderFilledCancelledFetcher implements AbstractOrderFilledCancelledFetcher { - private _exchangeWrapper: ExchangeWrapper; - private _defaultBlock: BlockParamLiteral; - constructor(exchange: ExchangeWrapper, defaultBlock: BlockParamLiteral) { - this._exchangeWrapper = exchange; - this._defaultBlock = defaultBlock; - } - public async getFilledTakerAmountAsync(orderHash: string): Promise { - const methodOpts = { - defaultBlock: this._defaultBlock, - }; - const filledTakerAmount = this._exchangeWrapper.getFilledTakerAmountAsync(orderHash, methodOpts); - return filledTakerAmount; - } - public async getCancelledTakerAmountAsync(orderHash: string): Promise { - const methodOpts = { - defaultBlock: this._defaultBlock, - }; - const cancelledTakerAmount = this._exchangeWrapper.getCancelledTakerAmountAsync(orderHash, methodOpts); - return cancelledTakerAmount; - } - public async getUnavailableTakerAmountAsync(orderHash: string): Promise { - const unavailableTakerAmount = await this._exchangeWrapper.getUnavailableTakerAmountAsync(orderHash); - return unavailableTakerAmount; - } - public getZRXTokenAddress(): string { - const zrxToken = this._exchangeWrapper.getZRXTokenAddress(); - return zrxToken; - } -} diff --git a/packages/contract-wrappers/src/stores/balance_proxy_allowance_lazy_store.ts b/packages/contract-wrappers/src/stores/balance_proxy_allowance_lazy_store.ts deleted file mode 100644 index c0250ce7c..000000000 --- a/packages/contract-wrappers/src/stores/balance_proxy_allowance_lazy_store.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { BlockParamLiteral } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; -import * as _ from 'lodash'; - -import { AbstractBalanceAndProxyAllowanceLazyStore } from '../abstract/abstract_balance_and_proxy_allowance_lazy_store'; -import { TokenWrapper } from '../contract_wrappers/token_wrapper'; - -/** - * Copy on read store for balances/proxyAllowances of tokens/accounts - */ -export class BalanceAndProxyAllowanceLazyStore implements AbstractBalanceAndProxyAllowanceLazyStore { - private _tokenWrapper: TokenWrapper; - private _defaultBlock: BlockParamLiteral; - private _balance: { - [tokenAddress: string]: { - [userAddress: string]: BigNumber; - }; - }; - private _proxyAllowance: { - [tokenAddress: string]: { - [userAddress: string]: BigNumber; - }; - }; - constructor(token: TokenWrapper, defaultBlock: BlockParamLiteral) { - this._tokenWrapper = token; - this._defaultBlock = defaultBlock; - this._balance = {}; - this._proxyAllowance = {}; - } - public async getBalanceAsync(tokenAddress: string, userAddress: string): Promise { - if (_.isUndefined(this._balance[tokenAddress]) || _.isUndefined(this._balance[tokenAddress][userAddress])) { - const methodOpts = { - defaultBlock: this._defaultBlock, - }; - const balance = await this._tokenWrapper.getBalanceAsync(tokenAddress, userAddress, methodOpts); - this.setBalance(tokenAddress, userAddress, balance); - } - const cachedBalance = this._balance[tokenAddress][userAddress]; - return cachedBalance; - } - public setBalance(tokenAddress: string, userAddress: string, balance: BigNumber): void { - if (_.isUndefined(this._balance[tokenAddress])) { - this._balance[tokenAddress] = {}; - } - this._balance[tokenAddress][userAddress] = balance; - } - public deleteBalance(tokenAddress: string, userAddress: string): void { - if (!_.isUndefined(this._balance[tokenAddress])) { - delete this._balance[tokenAddress][userAddress]; - if (_.isEmpty(this._balance[tokenAddress])) { - delete this._balance[tokenAddress]; - } - } - } - public async getProxyAllowanceAsync(tokenAddress: string, userAddress: string): Promise { - if ( - _.isUndefined(this._proxyAllowance[tokenAddress]) || - _.isUndefined(this._proxyAllowance[tokenAddress][userAddress]) - ) { - const methodOpts = { - defaultBlock: this._defaultBlock, - }; - const proxyAllowance = await this._tokenWrapper.getProxyAllowanceAsync( - tokenAddress, - userAddress, - methodOpts, - ); - this.setProxyAllowance(tokenAddress, userAddress, proxyAllowance); - } - const cachedProxyAllowance = this._proxyAllowance[tokenAddress][userAddress]; - return cachedProxyAllowance; - } - public setProxyAllowance(tokenAddress: string, userAddress: string, proxyAllowance: BigNumber): void { - if (_.isUndefined(this._proxyAllowance[tokenAddress])) { - this._proxyAllowance[tokenAddress] = {}; - } - this._proxyAllowance[tokenAddress][userAddress] = proxyAllowance; - } - public deleteProxyAllowance(tokenAddress: string, userAddress: string): void { - if (!_.isUndefined(this._proxyAllowance[tokenAddress])) { - delete this._proxyAllowance[tokenAddress][userAddress]; - if (_.isEmpty(this._proxyAllowance[tokenAddress])) { - delete this._proxyAllowance[tokenAddress]; - } - } - } - public deleteAll(): void { - this._balance = {}; - this._proxyAllowance = {}; - } -} diff --git a/packages/contract-wrappers/src/stores/order_filled_cancelled_lazy_store.ts b/packages/contract-wrappers/src/stores/order_filled_cancelled_lazy_store.ts deleted file mode 100644 index 3d96a95f3..000000000 --- a/packages/contract-wrappers/src/stores/order_filled_cancelled_lazy_store.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { AbstractOrderFilledCancelledFetcher } from '@0xproject/order-utils'; -import { BlockParamLiteral } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; -import * as _ from 'lodash'; - -import { ExchangeWrapper } from '../contract_wrappers/exchange_wrapper'; - -/** - * Copy on read store for filled/cancelled taker amounts - */ -export class OrderFilledCancelledLazyStore implements AbstractOrderFilledCancelledFetcher { - private _exchangeWrapper: ExchangeWrapper; - private _defaultBlock: BlockParamLiteral; - private _filledTakerAmount: { - [orderHash: string]: BigNumber; - }; - private _cancelledTakerAmount: { - [orderHash: string]: BigNumber; - }; - constructor(exchange: ExchangeWrapper, defaultBlock: BlockParamLiteral) { - this._exchangeWrapper = exchange; - this._defaultBlock = defaultBlock; - this._filledTakerAmount = {}; - this._cancelledTakerAmount = {}; - } - public async getFilledTakerAmountAsync(orderHash: string): Promise { - if (_.isUndefined(this._filledTakerAmount[orderHash])) { - const methodOpts = { - defaultBlock: this._defaultBlock, - }; - const filledTakerAmount = await this._exchangeWrapper.getFilledTakerAmountAsync(orderHash, methodOpts); - this.setFilledTakerAmount(orderHash, filledTakerAmount); - } - const cachedFilled = this._filledTakerAmount[orderHash]; - return cachedFilled; - } - public setFilledTakerAmount(orderHash: string, filledTakerAmount: BigNumber): void { - this._filledTakerAmount[orderHash] = filledTakerAmount; - } - public deleteFilledTakerAmount(orderHash: string): void { - delete this._filledTakerAmount[orderHash]; - } - public async getCancelledTakerAmountAsync(orderHash: string): Promise { - if (_.isUndefined(this._cancelledTakerAmount[orderHash])) { - const methodOpts = { - defaultBlock: this._defaultBlock, - }; - const cancelledTakerAmount = await this._exchangeWrapper.getCancelledTakerAmountAsync( - orderHash, - methodOpts, - ); - this.setCancelledTakerAmount(orderHash, cancelledTakerAmount); - } - const cachedCancelled = this._cancelledTakerAmount[orderHash]; - return cachedCancelled; - } - public setCancelledTakerAmount(orderHash: string, cancelledTakerAmount: BigNumber): void { - this._cancelledTakerAmount[orderHash] = cancelledTakerAmount; - } - public deleteCancelledTakerAmount(orderHash: string): void { - delete this._cancelledTakerAmount[orderHash]; - } - public deleteAll(): void { - this._filledTakerAmount = {}; - this._cancelledTakerAmount = {}; - } - public async getUnavailableTakerAmountAsync(orderHash: string): Promise { - const unavailableTakerAmount = await this._exchangeWrapper.getUnavailableTakerAmountAsync(orderHash); - return unavailableTakerAmount; - } - public getZRXTokenAddress(): string { - const zrxToken = this._exchangeWrapper.getZRXTokenAddress(); - return zrxToken; - } -} diff --git a/packages/contract-wrappers/src/utils/exchange_transfer_simulator.ts b/packages/contract-wrappers/src/utils/exchange_transfer_simulator.ts deleted file mode 100644 index 527b8575d..000000000 --- a/packages/contract-wrappers/src/utils/exchange_transfer_simulator.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { BlockParamLiteral, ExchangeContractErrs } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; - -import { AbstractBalanceAndProxyAllowanceLazyStore } from '../abstract/abstract_balance_and_proxy_allowance_lazy_store'; -import { TokenWrapper } from '../contract_wrappers/token_wrapper'; -import { TradeSide, TransferType } from '../types'; -import { constants } from '../utils/constants'; - -enum FailureReason { - Balance = 'balance', - ProxyAllowance = 'proxyAllowance', -} - -const ERR_MSG_MAPPING = { - [FailureReason.Balance]: { - [TradeSide.Maker]: { - [TransferType.Trade]: ExchangeContractErrs.InsufficientMakerBalance, - [TransferType.Fee]: ExchangeContractErrs.InsufficientMakerFeeBalance, - }, - [TradeSide.Taker]: { - [TransferType.Trade]: ExchangeContractErrs.InsufficientTakerBalance, - [TransferType.Fee]: ExchangeContractErrs.InsufficientTakerFeeBalance, - }, - }, - [FailureReason.ProxyAllowance]: { - [TradeSide.Maker]: { - [TransferType.Trade]: ExchangeContractErrs.InsufficientMakerAllowance, - [TransferType.Fee]: ExchangeContractErrs.InsufficientMakerFeeAllowance, - }, - [TradeSide.Taker]: { - [TransferType.Trade]: ExchangeContractErrs.InsufficientTakerAllowance, - [TransferType.Fee]: ExchangeContractErrs.InsufficientTakerFeeAllowance, - }, - }, -}; - -export class ExchangeTransferSimulator { - private _store: AbstractBalanceAndProxyAllowanceLazyStore; - private static _throwValidationError( - failureReason: FailureReason, - tradeSide: TradeSide, - transferType: TransferType, - ): never { - const errMsg = ERR_MSG_MAPPING[failureReason][tradeSide][transferType]; - throw new Error(errMsg); - } - constructor(store: AbstractBalanceAndProxyAllowanceLazyStore) { - this._store = store; - } - /** - * Simulates transferFrom call performed by a proxy - * @param tokenAddress Address of the token to be transferred - * @param from Owner of the transferred tokens - * @param to Recipient of the transferred tokens - * @param amountInBaseUnits The amount of tokens being transferred - * @param tradeSide Is Maker/Taker transferring - * @param transferType Is it a fee payment or a value transfer - */ - public async transferFromAsync( - tokenAddress: string, - from: string, - to: string, - amountInBaseUnits: BigNumber, - tradeSide: TradeSide, - transferType: TransferType, - ): Promise { - // HACK: When simulating an open order (e.g taker is NULL_ADDRESS), we don't want to adjust balances/ - // allowances for the taker. We do however, want to increase the balance of the maker since the maker - // might be relying on those funds to fill subsequent orders or pay the order's fees. - if (from === constants.NULL_ADDRESS && tradeSide === TradeSide.Taker) { - await this._increaseBalanceAsync(tokenAddress, to, amountInBaseUnits); - return; - } - const balance = await this._store.getBalanceAsync(tokenAddress, from); - const proxyAllowance = await this._store.getProxyAllowanceAsync(tokenAddress, from); - if (proxyAllowance.lessThan(amountInBaseUnits)) { - ExchangeTransferSimulator._throwValidationError(FailureReason.ProxyAllowance, tradeSide, transferType); - } - if (balance.lessThan(amountInBaseUnits)) { - ExchangeTransferSimulator._throwValidationError(FailureReason.Balance, tradeSide, transferType); - } - await this._decreaseProxyAllowanceAsync(tokenAddress, from, amountInBaseUnits); - await this._decreaseBalanceAsync(tokenAddress, from, amountInBaseUnits); - await this._increaseBalanceAsync(tokenAddress, to, amountInBaseUnits); - } - private async _decreaseProxyAllowanceAsync( - tokenAddress: string, - userAddress: string, - amountInBaseUnits: BigNumber, - ): Promise { - const proxyAllowance = await this._store.getProxyAllowanceAsync(tokenAddress, userAddress); - if (!proxyAllowance.eq(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) { - this._store.setProxyAllowance(tokenAddress, userAddress, proxyAllowance.minus(amountInBaseUnits)); - } - } - private async _increaseBalanceAsync( - tokenAddress: string, - userAddress: string, - amountInBaseUnits: BigNumber, - ): Promise { - const balance = await this._store.getBalanceAsync(tokenAddress, userAddress); - this._store.setBalance(tokenAddress, userAddress, balance.plus(amountInBaseUnits)); - } - private async _decreaseBalanceAsync( - tokenAddress: string, - userAddress: string, - amountInBaseUnits: BigNumber, - ): Promise { - const balance = await this._store.getBalanceAsync(tokenAddress, userAddress); - this._store.setBalance(tokenAddress, userAddress, balance.minus(amountInBaseUnits)); - } -} diff --git a/packages/contract-wrappers/src/utils/order_validation_utils.ts b/packages/contract-wrappers/src/utils/order_validation_utils.ts deleted file mode 100644 index c6ef26275..000000000 --- a/packages/contract-wrappers/src/utils/order_validation_utils.ts +++ /dev/null @@ -1,202 +0,0 @@ -import { getOrderHashHex, isValidSignature, OrderError } from '@0xproject/order-utils'; -import { ExchangeContractErrs, Order, SignedOrder } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; -import * as _ from 'lodash'; - -import { ExchangeContract } from '../contract_wrappers/generated/exchange'; -import { TradeSide, TransferType } from '../types'; -import { constants } from '../utils/constants'; -import { utils } from '../utils/utils'; - -import { ExchangeTransferSimulator } from './exchange_transfer_simulator'; - -export class OrderValidationUtils { - private _exchangeContract: ExchangeContract; - public static validateCancelOrderThrowIfInvalid( - order: Order, - cancelTakerTokenAmount: BigNumber, - unavailableTakerTokenAmount: BigNumber, - ): void { - if (cancelTakerTokenAmount.eq(0)) { - throw new Error(ExchangeContractErrs.OrderCancelAmountZero); - } - if (order.takerTokenAmount.eq(unavailableTakerTokenAmount)) { - throw new Error(ExchangeContractErrs.OrderAlreadyCancelledOrFilled); - } - const currentUnixTimestampSec = utils.getCurrentUnixTimestampSec(); - if (order.expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) { - throw new Error(ExchangeContractErrs.OrderCancelExpired); - } - } - public static async validateFillOrderBalancesAllowancesThrowIfInvalidAsync( - exchangeTradeEmulator: ExchangeTransferSimulator, - signedOrder: SignedOrder, - fillTakerTokenAmount: BigNumber, - senderAddress: string, - zrxTokenAddress: string, - ): Promise { - const fillMakerTokenAmount = OrderValidationUtils._getPartialAmount( - fillTakerTokenAmount, - signedOrder.takerTokenAmount, - signedOrder.makerTokenAmount, - ); - await exchangeTradeEmulator.transferFromAsync( - signedOrder.makerTokenAddress, - signedOrder.maker, - senderAddress, - fillMakerTokenAmount, - TradeSide.Maker, - TransferType.Trade, - ); - await exchangeTradeEmulator.transferFromAsync( - signedOrder.takerTokenAddress, - senderAddress, - signedOrder.maker, - fillTakerTokenAmount, - TradeSide.Taker, - TransferType.Trade, - ); - const makerFeeAmount = OrderValidationUtils._getPartialAmount( - fillTakerTokenAmount, - signedOrder.takerTokenAmount, - signedOrder.makerFee, - ); - await exchangeTradeEmulator.transferFromAsync( - zrxTokenAddress, - signedOrder.maker, - signedOrder.feeRecipient, - makerFeeAmount, - TradeSide.Maker, - TransferType.Fee, - ); - const takerFeeAmount = OrderValidationUtils._getPartialAmount( - fillTakerTokenAmount, - signedOrder.takerTokenAmount, - signedOrder.takerFee, - ); - await exchangeTradeEmulator.transferFromAsync( - zrxTokenAddress, - senderAddress, - signedOrder.feeRecipient, - takerFeeAmount, - TradeSide.Taker, - TransferType.Fee, - ); - } - private static _validateRemainingFillAmountNotZeroOrThrow( - takerTokenAmount: BigNumber, - unavailableTakerTokenAmount: BigNumber, - ): void { - if (takerTokenAmount.eq(unavailableTakerTokenAmount)) { - throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero); - } - } - private static _validateOrderNotExpiredOrThrow(expirationUnixTimestampSec: BigNumber): void { - const currentUnixTimestampSec = utils.getCurrentUnixTimestampSec(); - if (expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) { - throw new Error(ExchangeContractErrs.OrderFillExpired); - } - } - private static _getPartialAmount(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber { - const fillMakerTokenAmount = numerator - .mul(target) - .div(denominator) - .round(0); - return fillMakerTokenAmount; - } - constructor(exchangeContract: ExchangeContract) { - this._exchangeContract = exchangeContract; - } - public async validateOrderFillableOrThrowAsync( - exchangeTradeEmulator: ExchangeTransferSimulator, - signedOrder: SignedOrder, - zrxTokenAddress: string, - expectedFillTakerTokenAmount?: BigNumber, - ): Promise { - const orderHash = getOrderHashHex(signedOrder); - const unavailableTakerTokenAmount = await this._exchangeContract.getUnavailableTakerTokenAmount.callAsync( - orderHash, - ); - OrderValidationUtils._validateRemainingFillAmountNotZeroOrThrow( - signedOrder.takerTokenAmount, - unavailableTakerTokenAmount, - ); - OrderValidationUtils._validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec); - let fillTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount); - if (!_.isUndefined(expectedFillTakerTokenAmount)) { - fillTakerTokenAmount = expectedFillTakerTokenAmount; - } - await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( - exchangeTradeEmulator, - signedOrder, - fillTakerTokenAmount, - signedOrder.taker, - zrxTokenAddress, - ); - } - public async validateFillOrderThrowIfInvalidAsync( - exchangeTradeEmulator: ExchangeTransferSimulator, - signedOrder: SignedOrder, - fillTakerTokenAmount: BigNumber, - takerAddress: string, - zrxTokenAddress: string, - ): Promise { - if (fillTakerTokenAmount.eq(0)) { - throw new Error(ExchangeContractErrs.OrderFillAmountZero); - } - const orderHash = getOrderHashHex(signedOrder); - if (!isValidSignature(orderHash, signedOrder.ecSignature, signedOrder.maker)) { - throw new Error(OrderError.InvalidSignature); - } - const unavailableTakerTokenAmount = await this._exchangeContract.getUnavailableTakerTokenAmount.callAsync( - orderHash, - ); - OrderValidationUtils._validateRemainingFillAmountNotZeroOrThrow( - signedOrder.takerTokenAmount, - unavailableTakerTokenAmount, - ); - if (signedOrder.taker !== constants.NULL_ADDRESS && signedOrder.taker !== takerAddress) { - throw new Error(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker); - } - OrderValidationUtils._validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec); - const remainingTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount); - const filledTakerTokenAmount = remainingTakerTokenAmount.lessThan(fillTakerTokenAmount) - ? remainingTakerTokenAmount - : fillTakerTokenAmount; - await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( - exchangeTradeEmulator, - signedOrder, - filledTakerTokenAmount, - takerAddress, - zrxTokenAddress, - ); - - const wouldRoundingErrorOccur = await this._exchangeContract.isRoundingError.callAsync( - filledTakerTokenAmount, - signedOrder.takerTokenAmount, - signedOrder.makerTokenAmount, - ); - if (wouldRoundingErrorOccur) { - throw new Error(ExchangeContractErrs.OrderFillRoundingError); - } - return filledTakerTokenAmount; - } - public async validateFillOrKillOrderThrowIfInvalidAsync( - exchangeTradeEmulator: ExchangeTransferSimulator, - signedOrder: SignedOrder, - fillTakerTokenAmount: BigNumber, - takerAddress: string, - zrxTokenAddress: string, - ): Promise { - const filledTakerTokenAmount = await this.validateFillOrderThrowIfInvalidAsync( - exchangeTradeEmulator, - signedOrder, - fillTakerTokenAmount, - takerAddress, - zrxTokenAddress, - ); - if (filledTakerTokenAmount !== fillTakerTokenAmount) { - throw new Error(ExchangeContractErrs.InsufficientRemainingFillAmount); - } - } -} diff --git a/packages/contract-wrappers/test/exchange_transfer_simulator_test.ts b/packages/contract-wrappers/test/exchange_transfer_simulator_test.ts deleted file mode 100644 index cbc52df7f..000000000 --- a/packages/contract-wrappers/test/exchange_transfer_simulator_test.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { BlockchainLifecycle } from '@0xproject/dev-utils'; -import { BlockParamLiteral, Token } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; -import * as chai from 'chai'; - -import { ContractWrappers, ExchangeContractErrs } from '../src'; -import { BalanceAndProxyAllowanceLazyStore } from '../src/stores/balance_proxy_allowance_lazy_store'; -import { TradeSide, TransferType } from '../src/types'; -import { ExchangeTransferSimulator } from '../src/utils/exchange_transfer_simulator'; - -import { chaiSetup } from './utils/chai_setup'; -import { constants } from './utils/constants'; -import { provider, web3Wrapper } from './utils/web3_wrapper'; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); - -describe('ExchangeTransferSimulator', () => { - const config = { - networkId: constants.TESTRPC_NETWORK_ID, - }; - const contractWrappers = new ContractWrappers(provider, config); - const transferAmount = new BigNumber(5); - let userAddresses: string[]; - let tokens: Token[]; - let coinbase: string; - let sender: string; - let recipient: string; - let exampleTokenAddress: string; - let exchangeTransferSimulator: ExchangeTransferSimulator; - let txHash: string; - before(async () => { - userAddresses = await web3Wrapper.getAvailableAddressesAsync(); - [coinbase, sender, recipient] = userAddresses; - tokens = await contractWrappers.tokenRegistry.getTokensAsync(); - exampleTokenAddress = tokens[0].address; - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - describe('#transferFromAsync', () => { - beforeEach(() => { - const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( - contractWrappers.token, - BlockParamLiteral.Latest, - ); - exchangeTransferSimulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore); - }); - it("throws if the user doesn't have enough allowance", async () => { - return expect( - exchangeTransferSimulator.transferFromAsync( - exampleTokenAddress, - sender, - recipient, - transferAmount, - TradeSide.Taker, - TransferType.Trade, - ), - ).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerAllowance); - }); - it("throws if the user doesn't have enough balance", async () => { - txHash = await contractWrappers.token.setProxyAllowanceAsync(exampleTokenAddress, sender, transferAmount); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - return expect( - exchangeTransferSimulator.transferFromAsync( - exampleTokenAddress, - sender, - recipient, - transferAmount, - TradeSide.Maker, - TransferType.Trade, - ), - ).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerBalance); - }); - it('updates balances and proxyAllowance after transfer', async () => { - txHash = await contractWrappers.token.transferAsync(exampleTokenAddress, coinbase, sender, transferAmount); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - txHash = await contractWrappers.token.setProxyAllowanceAsync(exampleTokenAddress, sender, transferAmount); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - await exchangeTransferSimulator.transferFromAsync( - exampleTokenAddress, - sender, - recipient, - transferAmount, - TradeSide.Taker, - TransferType.Trade, - ); - const store = (exchangeTransferSimulator as any)._store; - const senderBalance = await store.getBalanceAsync(exampleTokenAddress, sender); - const recipientBalance = await store.getBalanceAsync(exampleTokenAddress, recipient); - const senderProxyAllowance = await store.getProxyAllowanceAsync(exampleTokenAddress, sender); - expect(senderBalance).to.be.bignumber.equal(0); - expect(recipientBalance).to.be.bignumber.equal(transferAmount); - expect(senderProxyAllowance).to.be.bignumber.equal(0); - }); - it("doesn't update proxyAllowance after transfer if unlimited", async () => { - txHash = await contractWrappers.token.transferAsync(exampleTokenAddress, coinbase, sender, transferAmount); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - txHash = await contractWrappers.token.setUnlimitedProxyAllowanceAsync(exampleTokenAddress, sender); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - await exchangeTransferSimulator.transferFromAsync( - exampleTokenAddress, - sender, - recipient, - transferAmount, - TradeSide.Taker, - TransferType.Trade, - ); - const store = (exchangeTransferSimulator as any)._store; - const senderBalance = await store.getBalanceAsync(exampleTokenAddress, sender); - const recipientBalance = await store.getBalanceAsync(exampleTokenAddress, recipient); - const senderProxyAllowance = await store.getProxyAllowanceAsync(exampleTokenAddress, sender); - expect(senderBalance).to.be.bignumber.equal(0); - expect(recipientBalance).to.be.bignumber.equal(transferAmount); - expect(senderProxyAllowance).to.be.bignumber.equal( - contractWrappers.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS, - ); - }); - }); -}); diff --git a/packages/contract-wrappers/test/exchange_wrapper_test.ts b/packages/contract-wrappers/test/exchange_wrapper_test.ts deleted file mode 100644 index cf69d4813..000000000 --- a/packages/contract-wrappers/test/exchange_wrapper_test.ts +++ /dev/null @@ -1,1227 +0,0 @@ -import { BlockchainLifecycle, callbackErrorReporter } from '@0xproject/dev-utils'; -import { FillScenarios } from '@0xproject/fill-scenarios'; -import { getOrderHashHex } from '@0xproject/order-utils'; -import { BlockParamLiteral, DoneCallback, OrderState } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; -import { Web3Wrapper } from '@0xproject/web3-wrapper'; -import * as chai from 'chai'; -import 'mocha'; - -import { - BlockRange, - ContractWrappers, - DecodedLogEvent, - ExchangeContractErrs, - ExchangeEvents, - LogCancelContractEventArgs, - LogFillContractEventArgs, - OrderCancellationRequest, - OrderFillRequest, - SignedOrder, - Token, -} from '../src'; - -import { chaiSetup } from './utils/chai_setup'; -import { constants } from './utils/constants'; -import { TokenUtils } from './utils/token_utils'; -import { provider, web3Wrapper } from './utils/web3_wrapper'; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); - -const NON_EXISTENT_ORDER_HASH = '0x79370342234e7acd6bbeac335bd3bb1d368383294b64b8160a00f4060e4d3777'; - -describe('ExchangeWrapper', () => { - let contractWrappers: ContractWrappers; - let tokenUtils: TokenUtils; - let tokens: Token[]; - let userAddresses: string[]; - let zrxTokenAddress: string; - let fillScenarios: FillScenarios; - let exchangeContractAddress: string; - const config = { - networkId: constants.TESTRPC_NETWORK_ID, - }; - before(async () => { - contractWrappers = new ContractWrappers(provider, config); - exchangeContractAddress = contractWrappers.exchange.getContractAddress(); - userAddresses = await web3Wrapper.getAvailableAddressesAsync(); - tokens = await contractWrappers.tokenRegistry.getTokensAsync(); - tokenUtils = new TokenUtils(tokens); - zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address; - fillScenarios = new FillScenarios(provider, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress); - await fillScenarios.initTokenBalancesAsync(); - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - describe('fillOrKill order(s)', () => { - let makerTokenAddress: string; - let takerTokenAddress: string; - let coinbase: string; - let makerAddress: string; - let takerAddress: string; - let feeRecipient: string; - const takerTokenFillAmount = new BigNumber(5); - before(async () => { - [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses; - tokens = await contractWrappers.tokenRegistry.getTokensAsync(); - const [makerToken, takerToken] = tokenUtils.getDummyTokens(); - makerTokenAddress = makerToken.address; - takerTokenAddress = takerToken.address; - }); - describe('#batchFillOrKillAsync', () => { - it('successfully batch fillOrKill', async () => { - const fillableAmount = new BigNumber(5); - const partialFillTakerAmount = new BigNumber(2); - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - const anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - const orderFillRequests = [ - { - signedOrder, - takerTokenFillAmount: partialFillTakerAmount, - }, - { - signedOrder: anotherSignedOrder, - takerTokenFillAmount: partialFillTakerAmount, - }, - ]; - await contractWrappers.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress); - }); - describe('order transaction options', () => { - let signedOrder: SignedOrder; - let orderFillRequests: OrderFillRequest[]; - const fillableAmount = new BigNumber(5); - beforeEach(async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - orderFillRequests = [ - { - signedOrder, - takerTokenFillAmount: new BigNumber(0), - }, - ]; - }); - it('should validate when orderTransactionOptions are not present', async () => { - return expect( - contractWrappers.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress), - ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); - it('should validate when orderTransactionOptions specify to validate', async () => { - return expect( - contractWrappers.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress, { - shouldValidate: true, - }), - ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); - it('should not validate when orderTransactionOptions specify not to validate', async () => { - return expect( - contractWrappers.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress, { - shouldValidate: false, - }), - ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); - }); - }); - describe('#fillOrKillOrderAsync', () => { - let signedOrder: SignedOrder; - const fillableAmount = new BigNumber(5); - beforeEach(async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - }); - describe('successful fills', () => { - it('should fill a valid order', async () => { - expect( - await contractWrappers.token.getBalanceAsync(makerTokenAddress, makerAddress), - ).to.be.bignumber.equal(fillableAmount); - expect( - await contractWrappers.token.getBalanceAsync(takerTokenAddress, makerAddress), - ).to.be.bignumber.equal(0); - expect( - await contractWrappers.token.getBalanceAsync(makerTokenAddress, takerAddress), - ).to.be.bignumber.equal(0); - expect( - await contractWrappers.token.getBalanceAsync(takerTokenAddress, takerAddress), - ).to.be.bignumber.equal(fillableAmount); - await contractWrappers.exchange.fillOrKillOrderAsync( - signedOrder, - takerTokenFillAmount, - takerAddress, - ); - expect( - await contractWrappers.token.getBalanceAsync(makerTokenAddress, makerAddress), - ).to.be.bignumber.equal(fillableAmount.minus(takerTokenFillAmount)); - expect( - await contractWrappers.token.getBalanceAsync(takerTokenAddress, makerAddress), - ).to.be.bignumber.equal(takerTokenFillAmount); - expect( - await contractWrappers.token.getBalanceAsync(makerTokenAddress, takerAddress), - ).to.be.bignumber.equal(takerTokenFillAmount); - expect( - await contractWrappers.token.getBalanceAsync(takerTokenAddress, takerAddress), - ).to.be.bignumber.equal(fillableAmount.minus(takerTokenFillAmount)); - }); - it('should partially fill a valid order', async () => { - const partialFillAmount = new BigNumber(3); - await contractWrappers.exchange.fillOrKillOrderAsync(signedOrder, partialFillAmount, takerAddress); - expect( - await contractWrappers.token.getBalanceAsync(makerTokenAddress, makerAddress), - ).to.be.bignumber.equal(fillableAmount.minus(partialFillAmount)); - expect( - await contractWrappers.token.getBalanceAsync(takerTokenAddress, makerAddress), - ).to.be.bignumber.equal(partialFillAmount); - expect( - await contractWrappers.token.getBalanceAsync(makerTokenAddress, takerAddress), - ).to.be.bignumber.equal(partialFillAmount); - expect( - await contractWrappers.token.getBalanceAsync(takerTokenAddress, takerAddress), - ).to.be.bignumber.equal(fillableAmount.minus(partialFillAmount)); - }); - }); - describe('order transaction options', () => { - const emptyFillableAmount = new BigNumber(0); - it('should validate when orderTransactionOptions are not present', async () => { - return expect( - contractWrappers.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress), - ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); - it('should validate when orderTransactionOptions specify to validate', async () => { - return expect( - contractWrappers.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress, { - shouldValidate: true, - }), - ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); - it('should not validate when orderTransactionOptions specify not to validate', async () => { - return expect( - contractWrappers.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress, { - shouldValidate: false, - }), - ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); - }); - }); - }); - describe('fill order(s)', () => { - let makerTokenAddress: string; - let takerTokenAddress: string; - let coinbase: string; - let makerAddress: string; - let takerAddress: string; - let feeRecipient: string; - const fillableAmount = new BigNumber(5); - const takerTokenFillAmount = new BigNumber(5); - const shouldThrowOnInsufficientBalanceOrAllowance = true; - before(async () => { - [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses; - tokens = await contractWrappers.tokenRegistry.getTokensAsync(); - const [makerToken, takerToken] = tokenUtils.getDummyTokens(); - makerTokenAddress = makerToken.address; - takerTokenAddress = takerToken.address; - }); - describe('#fillOrderAsync', () => { - describe('successful fills', () => { - it('should fill a valid order', async () => { - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - expect( - await contractWrappers.token.getBalanceAsync(makerTokenAddress, makerAddress), - ).to.be.bignumber.equal(fillableAmount); - expect( - await contractWrappers.token.getBalanceAsync(takerTokenAddress, makerAddress), - ).to.be.bignumber.equal(0); - expect( - await contractWrappers.token.getBalanceAsync(makerTokenAddress, takerAddress), - ).to.be.bignumber.equal(0); - expect( - await contractWrappers.token.getBalanceAsync(takerTokenAddress, takerAddress), - ).to.be.bignumber.equal(fillableAmount); - const txHash = await contractWrappers.exchange.fillOrderAsync( - signedOrder, - takerTokenFillAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - expect( - await contractWrappers.token.getBalanceAsync(makerTokenAddress, makerAddress), - ).to.be.bignumber.equal(fillableAmount.minus(takerTokenFillAmount)); - expect( - await contractWrappers.token.getBalanceAsync(takerTokenAddress, makerAddress), - ).to.be.bignumber.equal(takerTokenFillAmount); - expect( - await contractWrappers.token.getBalanceAsync(makerTokenAddress, takerAddress), - ).to.be.bignumber.equal(takerTokenFillAmount); - expect( - await contractWrappers.token.getBalanceAsync(takerTokenAddress, takerAddress), - ).to.be.bignumber.equal(fillableAmount.minus(takerTokenFillAmount)); - }); - it('should partially fill the valid order', async () => { - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - const partialFillAmount = new BigNumber(3); - const txHash = await contractWrappers.exchange.fillOrderAsync( - signedOrder, - partialFillAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - expect( - await contractWrappers.token.getBalanceAsync(makerTokenAddress, makerAddress), - ).to.be.bignumber.equal(fillableAmount.minus(partialFillAmount)); - expect( - await contractWrappers.token.getBalanceAsync(takerTokenAddress, makerAddress), - ).to.be.bignumber.equal(partialFillAmount); - expect( - await contractWrappers.token.getBalanceAsync(makerTokenAddress, takerAddress), - ).to.be.bignumber.equal(partialFillAmount); - expect( - await contractWrappers.token.getBalanceAsync(takerTokenAddress, takerAddress), - ).to.be.bignumber.equal(fillableAmount.minus(partialFillAmount)); - }); - it('should fill the valid orders with fees', async () => { - const makerFee = new BigNumber(1); - const takerFee = new BigNumber(2); - const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - makerTokenAddress, - takerTokenAddress, - makerFee, - takerFee, - makerAddress, - takerAddress, - fillableAmount, - feeRecipient, - ); - const txHash = await contractWrappers.exchange.fillOrderAsync( - signedOrder, - takerTokenFillAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - expect( - await contractWrappers.token.getBalanceAsync(zrxTokenAddress, feeRecipient), - ).to.be.bignumber.equal(makerFee.plus(takerFee)); - }); - }); - describe('order transaction options', () => { - let signedOrder: SignedOrder; - const emptyFillTakerAmount = new BigNumber(0); - beforeEach(async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - }); - it('should validate when orderTransactionOptions are not present', async () => { - return expect( - contractWrappers.exchange.fillOrderAsync( - signedOrder, - emptyFillTakerAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ), - ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); - it('should validate when orderTransactionOptions specify to validate', async () => { - return expect( - contractWrappers.exchange.fillOrderAsync( - signedOrder, - emptyFillTakerAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - { - shouldValidate: true, - }, - ), - ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); - it('should not validate when orderTransactionOptions specify not to validate', async () => { - return expect( - contractWrappers.exchange.fillOrderAsync( - signedOrder, - emptyFillTakerAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - { - shouldValidate: false, - }, - ), - ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); - }); - describe('negative fill amount', async () => { - let signedOrder: SignedOrder; - const negativeFillTakerAmount = new BigNumber(-100); - beforeEach(async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - }); - it('should not allow the exchange wrapper to fill if amount is negative', async () => { - return expect( - contractWrappers.exchange.fillOrderAsync( - signedOrder, - negativeFillTakerAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ), - ).to.be.rejected(); - }); - }); - }); - describe('#batchFillOrdersAsync', () => { - let signedOrder: SignedOrder; - let signedOrderHashHex: string; - let anotherSignedOrder: SignedOrder; - let anotherOrderHashHex: string; - let orderFillBatch: OrderFillRequest[]; - beforeEach(async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - signedOrderHashHex = getOrderHashHex(signedOrder); - anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - anotherOrderHashHex = getOrderHashHex(anotherSignedOrder); - }); - describe('successful batch fills', () => { - beforeEach(() => { - orderFillBatch = [ - { - signedOrder, - takerTokenFillAmount, - }, - { - signedOrder: anotherSignedOrder, - takerTokenFillAmount, - }, - ]; - }); - it('should throw if a batch is empty', async () => { - return expect( - contractWrappers.exchange.batchFillOrdersAsync( - [], - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ), - ).to.be.rejectedWith(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem); - }); - it('should successfully fill multiple orders', async () => { - const txHash = await contractWrappers.exchange.batchFillOrdersAsync( - orderFillBatch, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - const filledAmount = await contractWrappers.exchange.getFilledTakerAmountAsync(signedOrderHashHex); - const anotherFilledAmount = await contractWrappers.exchange.getFilledTakerAmountAsync( - anotherOrderHashHex, - ); - expect(filledAmount).to.be.bignumber.equal(takerTokenFillAmount); - expect(anotherFilledAmount).to.be.bignumber.equal(takerTokenFillAmount); - }); - }); - describe('order transaction options', () => { - beforeEach(async () => { - const emptyFillTakerAmount = new BigNumber(0); - orderFillBatch = [ - { - signedOrder, - takerTokenFillAmount: emptyFillTakerAmount, - }, - { - signedOrder: anotherSignedOrder, - takerTokenFillAmount: emptyFillTakerAmount, - }, - ]; - }); - it('should validate when orderTransactionOptions are not present', async () => { - return expect( - contractWrappers.exchange.batchFillOrdersAsync( - orderFillBatch, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ), - ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); - it('should validate when orderTransactionOptions specify to validate', async () => { - return expect( - contractWrappers.exchange.batchFillOrdersAsync( - orderFillBatch, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - { - shouldValidate: true, - }, - ), - ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); - it('should not validate when orderTransactionOptions specify not to validate', async () => { - return expect( - contractWrappers.exchange.batchFillOrdersAsync( - orderFillBatch, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - { - shouldValidate: false, - }, - ), - ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); - }); - describe('negative batch fill amount', async () => { - beforeEach(async () => { - const negativeFillTakerAmount = new BigNumber(-100); - orderFillBatch = [ - { - signedOrder, - takerTokenFillAmount, - }, - { - signedOrder: anotherSignedOrder, - takerTokenFillAmount: negativeFillTakerAmount, - }, - ]; - }); - it('should not allow the exchange wrapper to batch fill if any amount is negative', async () => { - return expect( - contractWrappers.exchange.batchFillOrdersAsync( - orderFillBatch, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ), - ).to.be.rejected(); - }); - }); - }); - describe('#fillOrdersUpTo', () => { - let signedOrder: SignedOrder; - let signedOrderHashHex: string; - let anotherSignedOrder: SignedOrder; - let anotherOrderHashHex: string; - let signedOrders: SignedOrder[]; - const fillUpToAmount = fillableAmount.plus(fillableAmount).minus(1); - beforeEach(async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - signedOrderHashHex = getOrderHashHex(signedOrder); - anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - anotherOrderHashHex = getOrderHashHex(anotherSignedOrder); - signedOrders = [signedOrder, anotherSignedOrder]; - }); - describe('successful batch fills', () => { - it('should throw if a batch is empty', async () => { - return expect( - contractWrappers.exchange.fillOrdersUpToAsync( - [], - fillUpToAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ), - ).to.be.rejectedWith(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem); - }); - it('should successfully fill up to specified amount when all orders are fully funded', async () => { - const txHash = await contractWrappers.exchange.fillOrdersUpToAsync( - signedOrders, - fillUpToAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - const filledAmount = await contractWrappers.exchange.getFilledTakerAmountAsync(signedOrderHashHex); - const anotherFilledAmount = await contractWrappers.exchange.getFilledTakerAmountAsync( - anotherOrderHashHex, - ); - expect(filledAmount).to.be.bignumber.equal(fillableAmount); - const remainingFillAmount = fillableAmount.minus(1); - expect(anotherFilledAmount).to.be.bignumber.equal(remainingFillAmount); - }); - it('should successfully fill up to specified amount and leave the rest of the orders untouched', async () => { - const txHash = await contractWrappers.exchange.fillOrdersUpToAsync( - signedOrders, - fillableAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - const filledAmount = await contractWrappers.exchange.getFilledTakerAmountAsync(signedOrderHashHex); - const zeroAmount = await contractWrappers.exchange.getFilledTakerAmountAsync(anotherOrderHashHex); - expect(filledAmount).to.be.bignumber.equal(fillableAmount); - expect(zeroAmount).to.be.bignumber.equal(0); - }); - it('should successfully fill up to specified amount even if filling all orders would fail', async () => { - const missingBalance = new BigNumber(1); // User will still have enough balance to fill up to 9, - // but won't have 10 to fully fill all orders in a batch. - await contractWrappers.token.transferAsync( - makerTokenAddress, - makerAddress, - coinbase, - missingBalance, - ); - const txHash = await contractWrappers.exchange.fillOrdersUpToAsync( - signedOrders, - fillUpToAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - const filledAmount = await contractWrappers.exchange.getFilledTakerAmountAsync(signedOrderHashHex); - const anotherFilledAmount = await contractWrappers.exchange.getFilledTakerAmountAsync( - anotherOrderHashHex, - ); - expect(filledAmount).to.be.bignumber.equal(fillableAmount); - const remainingFillAmount = fillableAmount.minus(1); - expect(anotherFilledAmount).to.be.bignumber.equal(remainingFillAmount); - }); - }); - describe('failed batch fills', () => { - it("should fail validation if user doesn't have enough balance without fill up to", async () => { - const missingBalance = new BigNumber(2); // User will only have enough balance to fill up to 8 - await contractWrappers.token.transferAsync( - makerTokenAddress, - makerAddress, - coinbase, - missingBalance, - ); - return expect( - contractWrappers.exchange.fillOrdersUpToAsync( - signedOrders, - fillUpToAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ), - ).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerBalance); - }); - }); - describe('order transaction options', () => { - const emptyFillUpToAmount = new BigNumber(0); - it('should validate when orderTransactionOptions are not present', async () => { - return expect( - contractWrappers.exchange.fillOrdersUpToAsync( - signedOrders, - emptyFillUpToAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ), - ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); - it('should validate when orderTransactionOptions specify to validate', async () => { - return expect( - contractWrappers.exchange.fillOrdersUpToAsync( - signedOrders, - emptyFillUpToAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - { - shouldValidate: true, - }, - ), - ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); - it('should not validate when orderTransactionOptions specify not to validate', async () => { - return expect( - contractWrappers.exchange.fillOrdersUpToAsync( - signedOrders, - emptyFillUpToAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - { - shouldValidate: false, - }, - ), - ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); - }); - }); - }); - describe('cancel order(s)', () => { - 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.getDummyTokens(); - makerTokenAddress = makerToken.address; - takerTokenAddress = takerToken.address; - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - orderHashHex = getOrderHashHex(signedOrder); - }); - describe('#cancelOrderAsync', () => { - describe('successful cancels', () => { - it('should cancel an order', async () => { - const txHash = await contractWrappers.exchange.cancelOrderAsync(signedOrder, cancelAmount); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - const cancelledAmount = await contractWrappers.exchange.getCancelledTakerAmountAsync(orderHashHex); - expect(cancelledAmount).to.be.bignumber.equal(cancelAmount); - }); - }); - describe('order transaction options', () => { - const emptyCancelTakerTokenAmount = new BigNumber(0); - it('should validate when orderTransactionOptions are not present', async () => { - return expect( - contractWrappers.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount), - ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); - }); - it('should validate when orderTransactionOptions specify to validate', async () => { - return expect( - contractWrappers.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount, { - shouldValidate: true, - }), - ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); - }); - it('should not validate when orderTransactionOptions specify not to validate', async () => { - return expect( - contractWrappers.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount, { - shouldValidate: false, - }), - ).to.not.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); - }); - }); - }); - describe('#batchCancelOrdersAsync', () => { - let anotherSignedOrder: SignedOrder; - let anotherOrderHashHex: string; - let cancelBatch: OrderCancellationRequest[]; - beforeEach(async () => { - anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - anotherOrderHashHex = getOrderHashHex(anotherSignedOrder); - cancelBatch = [ - { - order: signedOrder, - takerTokenCancelAmount: cancelAmount, - }, - { - order: anotherSignedOrder, - takerTokenCancelAmount: cancelAmount, - }, - ]; - }); - describe('failed batch cancels', () => { - it('should throw when orders have different makers', async () => { - const signedOrderWithDifferentMaker = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - takerAddress, - takerAddress, - fillableAmount, - ); - return expect( - contractWrappers.exchange.batchCancelOrdersAsync([ - cancelBatch[0], - { - order: signedOrderWithDifferentMaker, - takerTokenCancelAmount: cancelAmount, - }, - ]), - ).to.be.rejectedWith(ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed); - }); - }); - describe('successful batch cancels', () => { - it('should cancel a batch of orders', async () => { - await contractWrappers.exchange.batchCancelOrdersAsync(cancelBatch); - const cancelledAmount = await contractWrappers.exchange.getCancelledTakerAmountAsync(orderHashHex); - const anotherCancelledAmount = await contractWrappers.exchange.getCancelledTakerAmountAsync( - anotherOrderHashHex, - ); - expect(cancelledAmount).to.be.bignumber.equal(cancelAmount); - expect(anotherCancelledAmount).to.be.bignumber.equal(cancelAmount); - }); - }); - describe('order transaction options', () => { - beforeEach(async () => { - const emptyTakerTokenCancelAmount = new BigNumber(0); - cancelBatch = [ - { - order: signedOrder, - takerTokenCancelAmount: emptyTakerTokenCancelAmount, - }, - { - order: anotherSignedOrder, - takerTokenCancelAmount: emptyTakerTokenCancelAmount, - }, - ]; - }); - it('should validate when orderTransactionOptions are not present', async () => { - return expect(contractWrappers.exchange.batchCancelOrdersAsync(cancelBatch)).to.be.rejectedWith( - ExchangeContractErrs.OrderCancelAmountZero, - ); - }); - it('should validate when orderTransactionOptions specify to validate', async () => { - return expect( - contractWrappers.exchange.batchCancelOrdersAsync(cancelBatch, { - shouldValidate: true, - }), - ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); - }); - it('should not validate when orderTransactionOptions specify not to validate', async () => { - return expect( - contractWrappers.exchange.batchCancelOrdersAsync(cancelBatch, { - shouldValidate: false, - }), - ).to.not.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); - }); - }); - }); - }); - describe('tests that require partially filled order', () => { - let makerTokenAddress: string; - let takerTokenAddress: string; - let takerAddress: string; - let fillableAmount: BigNumber; - let partialFillAmount: BigNumber; - let signedOrder: SignedOrder; - let orderHash: string; - before(() => { - takerAddress = userAddresses[1]; - tokenUtils = new TokenUtils(tokens); - const [makerToken, takerToken] = tokenUtils.getDummyTokens(); - makerTokenAddress = makerToken.address; - takerTokenAddress = takerToken.address; - }); - beforeEach(async () => { - fillableAmount = new BigNumber(5); - partialFillAmount = new BigNumber(2); - signedOrder = await fillScenarios.createPartiallyFilledSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - takerAddress, - fillableAmount, - partialFillAmount, - ); - orderHash = getOrderHashHex(signedOrder); - }); - describe('#getUnavailableTakerAmountAsync', () => { - it('should throw if passed an invalid orderHash', async () => { - const invalidOrderHashHex = '0x123'; - return expect( - contractWrappers.exchange.getUnavailableTakerAmountAsync(invalidOrderHashHex), - ).to.be.rejected(); - }); - it('should return zero if passed a valid but non-existent orderHash', async () => { - const unavailableValueT = await contractWrappers.exchange.getUnavailableTakerAmountAsync( - NON_EXISTENT_ORDER_HASH, - ); - expect(unavailableValueT).to.be.bignumber.equal(0); - }); - it('should return the unavailableValueT for a valid and partially filled orderHash', async () => { - const unavailableValueT = await contractWrappers.exchange.getUnavailableTakerAmountAsync(orderHash); - expect(unavailableValueT).to.be.bignumber.equal(partialFillAmount); - }); - }); - describe('#getFilledTakerAmountAsync', () => { - it('should throw if passed an invalid orderHash', async () => { - const invalidOrderHashHex = '0x123'; - return expect( - contractWrappers.exchange.getFilledTakerAmountAsync(invalidOrderHashHex), - ).to.be.rejected(); - }); - it('should return zero if passed a valid but non-existent orderHash', async () => { - const filledValueT = await contractWrappers.exchange.getFilledTakerAmountAsync(NON_EXISTENT_ORDER_HASH); - expect(filledValueT).to.be.bignumber.equal(0); - }); - it('should return the filledValueT for a valid and partially filled orderHash', async () => { - const filledValueT = await contractWrappers.exchange.getFilledTakerAmountAsync(orderHash); - expect(filledValueT).to.be.bignumber.equal(partialFillAmount); - }); - }); - describe('#getCancelledTakerAmountAsync', () => { - it('should throw if passed an invalid orderHash', async () => { - const invalidOrderHashHex = '0x123'; - return expect( - contractWrappers.exchange.getCancelledTakerAmountAsync(invalidOrderHashHex), - ).to.be.rejected(); - }); - it('should return zero if passed a valid but non-existent orderHash', async () => { - const cancelledValueT = await contractWrappers.exchange.getCancelledTakerAmountAsync( - NON_EXISTENT_ORDER_HASH, - ); - expect(cancelledValueT).to.be.bignumber.equal(0); - }); - it('should return the cancelledValueT for a valid and partially filled orderHash', async () => { - const cancelledValueT = await contractWrappers.exchange.getCancelledTakerAmountAsync(orderHash); - expect(cancelledValueT).to.be.bignumber.equal(0); - }); - it('should return the cancelledValueT for a valid and cancelled orderHash', async () => { - const cancelAmount = fillableAmount.minus(partialFillAmount); - await contractWrappers.exchange.cancelOrderAsync(signedOrder, cancelAmount); - const cancelledValueT = await contractWrappers.exchange.getCancelledTakerAmountAsync(orderHash); - expect(cancelledValueT).to.be.bignumber.equal(cancelAmount); - }); - }); - }); - describe('#subscribe', () => { - const indexFilterValues = {}; - const shouldThrowOnInsufficientBalanceOrAllowance = true; - let makerTokenAddress: string; - let takerTokenAddress: string; - let coinbase: string; - let takerAddress: string; - let makerAddress: string; - let fillableAmount: BigNumber; - let signedOrder: SignedOrder; - const takerTokenFillAmountInBaseUnits = new BigNumber(1); - const cancelTakerAmountInBaseUnits = new BigNumber(1); - before(() => { - [coinbase, makerAddress, takerAddress] = userAddresses; - const [makerToken, takerToken] = tokenUtils.getDummyTokens(); - makerTokenAddress = makerToken.address; - takerTokenAddress = takerToken.address; - }); - beforeEach(async () => { - fillableAmount = new BigNumber(5); - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - }); - afterEach(async () => { - contractWrappers.exchange.unsubscribeAll(); - }); - // Hack: Mocha does not allow a test to be both async and have a `done` callback - // Since we need to await the receipt of the event in the `subscribe` callback, - // we do need both. A hack is to make the top-level a sync fn w/ a done callback and then - // wrap the rest of the test in an async block - // Source: https://github.com/mochajs/mocha/issues/2407 - it('Should receive the LogFill event when an order is filled', (done: DoneCallback) => { - (async () => { - const callback = callbackErrorReporter.reportNodeCallbackErrors(done)( - (logEvent: DecodedLogEvent) => { - expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogFill); - }, - ); - contractWrappers.exchange.subscribe(ExchangeEvents.LogFill, indexFilterValues, callback); - await contractWrappers.exchange.fillOrderAsync( - signedOrder, - takerTokenFillAmountInBaseUnits, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ); - })().catch(done); - }); - it('Should receive the LogCancel event when an order is cancelled', (done: DoneCallback) => { - (async () => { - const callback = callbackErrorReporter.reportNodeCallbackErrors(done)( - (logEvent: DecodedLogEvent) => { - expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogCancel); - }, - ); - contractWrappers.exchange.subscribe(ExchangeEvents.LogCancel, indexFilterValues, callback); - await contractWrappers.exchange.cancelOrderAsync(signedOrder, cancelTakerAmountInBaseUnits); - })().catch(done); - }); - it('Outstanding subscriptions are cancelled when contractWrappers.setProvider called', (done: DoneCallback) => { - (async () => { - const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)( - (logEvent: DecodedLogEvent) => { - done(new Error('Expected this subscription to have been cancelled')); - }, - ); - contractWrappers.exchange.subscribe(ExchangeEvents.LogFill, indexFilterValues, callbackNeverToBeCalled); - - contractWrappers.setProvider(provider, constants.TESTRPC_NETWORK_ID); - - const callback = callbackErrorReporter.reportNodeCallbackErrors(done)( - (logEvent: DecodedLogEvent) => { - expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogFill); - }, - ); - contractWrappers.exchange.subscribe(ExchangeEvents.LogFill, indexFilterValues, callback); - await contractWrappers.exchange.fillOrderAsync( - signedOrder, - takerTokenFillAmountInBaseUnits, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ); - })().catch(done); - }); - it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => { - (async () => { - const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)( - (logEvent: DecodedLogEvent) => { - done(new Error('Expected this subscription to have been cancelled')); - }, - ); - const subscriptionToken = contractWrappers.exchange.subscribe( - ExchangeEvents.LogFill, - indexFilterValues, - callbackNeverToBeCalled, - ); - contractWrappers.exchange.unsubscribe(subscriptionToken); - await contractWrappers.exchange.fillOrderAsync( - signedOrder, - takerTokenFillAmountInBaseUnits, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ); - done(); - })().catch(done); - }); - }); - describe('#getOrderHashHexUsingContractCallAsync', () => { - let makerTokenAddress: string; - let takerTokenAddress: string; - let makerAddress: string; - let takerAddress: string; - const fillableAmount = new BigNumber(5); - before(async () => { - [, makerAddress, takerAddress] = userAddresses; - const [makerToken, takerToken] = tokenUtils.getDummyTokens(); - makerTokenAddress = makerToken.address; - takerTokenAddress = takerToken.address; - }); - it("get's the same hash as the local function", async () => { - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - const orderHash = getOrderHashHex(signedOrder); - const orderHashFromContract = await (contractWrappers.exchange as any)._getOrderHashHexUsingContractCallAsync( - signedOrder, - ); - expect(orderHash).to.equal(orderHashFromContract); - }); - }); - describe('#getZRXTokenAddressAsync', () => { - it('gets the same token as is in token registry', () => { - const zrxAddress = contractWrappers.exchange.getZRXTokenAddress(); - const zrxToken = tokenUtils.getProtocolTokenOrThrow(); - expect(zrxAddress).to.equal(zrxToken.address); - }); - }); - describe('#getLogsAsync', () => { - let makerTokenAddress: string; - let takerTokenAddress: string; - let makerAddress: string; - let takerAddress: string; - const fillableAmount = new BigNumber(5); - const shouldThrowOnInsufficientBalanceOrAllowance = true; - const blockRange: BlockRange = { - fromBlock: 0, - toBlock: BlockParamLiteral.Latest, - }; - let txHash: string; - before(async () => { - [, makerAddress, takerAddress] = userAddresses; - const [makerToken, takerToken] = tokenUtils.getDummyTokens(); - makerTokenAddress = makerToken.address; - takerTokenAddress = takerToken.address; - }); - it('should get logs with decoded args emitted by LogFill', async () => { - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - txHash = await contractWrappers.exchange.fillOrderAsync( - signedOrder, - fillableAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - const eventName = ExchangeEvents.LogFill; - const indexFilterValues = {}; - const logs = await contractWrappers.exchange.getLogsAsync(eventName, blockRange, indexFilterValues); - expect(logs).to.have.length(1); - expect(logs[0].event).to.be.equal(eventName); - }); - it('should only get the logs with the correct event name', async () => { - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - txHash = await contractWrappers.exchange.fillOrderAsync( - signedOrder, - fillableAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - const differentEventName = ExchangeEvents.LogCancel; - const indexFilterValues = {}; - const logs = await contractWrappers.exchange.getLogsAsync( - differentEventName, - blockRange, - indexFilterValues, - ); - expect(logs).to.have.length(0); - }); - it('should only get the logs with the correct indexed fields', async () => { - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - txHash = await contractWrappers.exchange.fillOrderAsync( - signedOrder, - fillableAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - - const differentMakerAddress = userAddresses[2]; - const anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - differentMakerAddress, - takerAddress, - fillableAmount, - ); - txHash = await contractWrappers.exchange.fillOrderAsync( - anotherSignedOrder, - fillableAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - - const eventName = ExchangeEvents.LogFill; - const indexFilterValues = { - maker: differentMakerAddress, - }; - const logs = await contractWrappers.exchange.getLogsAsync( - eventName, - blockRange, - indexFilterValues, - ); - expect(logs).to.have.length(1); - const args = logs[0].args; - expect(args.maker).to.be.equal(differentMakerAddress); - }); - }); - describe('#getOrderStateAsync', () => { - let maker: string; - let taker: string; - let makerToken: Token; - let takerToken: Token; - let signedOrder: SignedOrder; - let orderState: OrderState; - const fillableAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(5), constants.ZRX_DECIMALS); - before(async () => { - [, maker, taker] = userAddresses; - tokens = await contractWrappers.tokenRegistry.getTokensAsync(); - [makerToken, takerToken] = tokenUtils.getDummyTokens(); - }); - it('should report orderStateValid when order is fillable', async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerToken.address, - takerToken.address, - maker, - taker, - fillableAmount, - ); - orderState = await contractWrappers.exchange.getOrderStateAsync(signedOrder); - expect(orderState.isValid).to.be.true(); - }); - it('should report orderStateInvalid when maker allowance set to 0', async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerToken.address, - takerToken.address, - maker, - taker, - fillableAmount, - ); - await contractWrappers.token.setProxyAllowanceAsync(makerToken.address, maker, new BigNumber(0)); - orderState = await contractWrappers.exchange.getOrderStateAsync(signedOrder); - expect(orderState.isValid).to.be.false(); - }); - }); -}); // tslint:disable:max-file-line-count diff --git a/packages/contract-wrappers/test/order_validation_test.ts b/packages/contract-wrappers/test/order_validation_test.ts deleted file mode 100644 index 2afea2d5f..000000000 --- a/packages/contract-wrappers/test/order_validation_test.ts +++ /dev/null @@ -1,539 +0,0 @@ -import { BlockchainLifecycle } from '@0xproject/dev-utils'; -import { FillScenarios } from '@0xproject/fill-scenarios'; -import { OrderError } from '@0xproject/order-utils'; -import { BlockParamLiteral } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; -import * as chai from 'chai'; -import * as Sinon from 'sinon'; - -import { ContractWrappers, ExchangeContractErrs, SignedOrder, Token } from '../src'; -import { BalanceAndProxyAllowanceLazyStore } from '../src/stores/balance_proxy_allowance_lazy_store'; -import { TradeSide, TransferType } from '../src/types'; -import { ExchangeTransferSimulator } from '../src/utils/exchange_transfer_simulator'; -import { OrderValidationUtils } from '../src/utils/order_validation_utils'; - -import { chaiSetup } from './utils/chai_setup'; -import { constants } from './utils/constants'; -import { TokenUtils } from './utils/token_utils'; -import { provider, web3Wrapper } from './utils/web3_wrapper'; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); - -describe('OrderValidation', () => { - let contractWrappers: ContractWrappers; - 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 config = { - networkId: constants.TESTRPC_NETWORK_ID, - }; - before(async () => { - contractWrappers = new ContractWrappers(provider, config); - exchangeContractAddress = contractWrappers.exchange.getContractAddress(); - userAddresses = await web3Wrapper.getAvailableAddressesAsync(); - [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses; - tokens = await contractWrappers.tokenRegistry.getTokensAsync(); - tokenUtils = new TokenUtils(tokens); - zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address; - fillScenarios = new FillScenarios(provider, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress); - const [makerToken, takerToken] = tokenUtils.getDummyTokens(); - makerTokenAddress = makerToken.address; - takerTokenAddress = takerToken.address; - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - describe('validateOrderFillableOrThrowAsync', () => { - it('should succeed if the order is fillable', async () => { - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - await contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder); - }); - it('should succeed if the maker is buying ZRX and has no ZRX balance', async () => { - const makerFee = new BigNumber(2); - const takerFee = new BigNumber(2); - const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - makerTokenAddress, - zrxTokenAddress, - makerFee, - takerFee, - makerAddress, - takerAddress, - fillableAmount, - feeRecipient, - ); - const zrxMakerBalance = await contractWrappers.token.getBalanceAsync(zrxTokenAddress, makerAddress); - await contractWrappers.token.transferAsync(zrxTokenAddress, makerAddress, takerAddress, zrxMakerBalance); - await contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder); - }); - it('should succeed if the maker is buying ZRX and has no ZRX balance and there is no specified taker', async () => { - const makerFee = new BigNumber(2); - const takerFee = new BigNumber(2); - const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - makerTokenAddress, - zrxTokenAddress, - makerFee, - takerFee, - makerAddress, - constants.NULL_ADDRESS, - fillableAmount, - feeRecipient, - ); - const zrxMakerBalance = await contractWrappers.token.getBalanceAsync(zrxTokenAddress, makerAddress); - await contractWrappers.token.transferAsync(zrxTokenAddress, makerAddress, takerAddress, zrxMakerBalance); - await contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder); - }); - it('should succeed if the order is asymmetric and fillable', async () => { - const makerFillableAmount = fillableAmount; - // tslint:disable-next-line:custom-no-magic-numbers - const takerFillableAmount = fillableAmount.minus(4); - const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - makerFillableAmount, - takerFillableAmount, - ); - await contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder); - }); - it('should throw when the order is fully filled or cancelled', async () => { - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - await contractWrappers.exchange.cancelOrderAsync(signedOrder, fillableAmount); - return expect(contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder)).to.be.rejectedWith( - ExchangeContractErrs.OrderRemainingFillAmountZero, - ); - }); - 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(contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder)).to.be.rejectedWith( - ExchangeContractErrs.OrderFillExpired, - ); - }); - }); - describe('validateFillOrderAndThrowIfInvalidAsync', () => { - 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( - contractWrappers.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 - // tslint:disable-next-line:custom-no-magic-numbers - signedOrder.ecSignature.v = 28 - signedOrder.ecSignature.v + 27; - return expect( - contractWrappers.exchange.validateFillOrderThrowIfInvalidAsync( - signedOrder, - fillableAmount, - takerAddress, - ), - ).to.be.rejectedWith(OrderError.InvalidSignature); - }); - it('should throw when the order is fully filled or cancelled', async () => { - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - await contractWrappers.exchange.cancelOrderAsync(signedOrder, fillableAmount); - return expect( - contractWrappers.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, - ); - // tslint:disable-next-line:custom-no-magic-numbers - const nonTakerAddress = userAddresses[6]; - return expect( - contractWrappers.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( - contractWrappers.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( - contractWrappers.exchange.validateFillOrderThrowIfInvalidAsync( - signedOrder, - fillTakerAmountThatCausesRoundingError, - takerAddress, - ), - ).to.be.rejectedWith(ExchangeContractErrs.OrderFillRoundingError); - }); - }); - describe('#validateFillOrKillOrderAndThrowIfInvalidAsync', () => { - it('should throw if remaining fillAmount is less then the desired fillAmount', async () => { - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - const tooLargeFillAmount = new BigNumber(7); - const fillAmountDifference = tooLargeFillAmount.minus(fillableAmount); - await contractWrappers.token.transferAsync(takerTokenAddress, coinbase, takerAddress, fillAmountDifference); - await contractWrappers.token.setProxyAllowanceAsync(takerTokenAddress, takerAddress, tooLargeFillAmount); - await contractWrappers.token.transferAsync(makerTokenAddress, coinbase, makerAddress, fillAmountDifference); - await contractWrappers.token.setProxyAllowanceAsync(makerTokenAddress, makerAddress, tooLargeFillAmount); - - return expect( - contractWrappers.exchange.validateFillOrKillOrderThrowIfInvalidAsync( - signedOrder, - tooLargeFillAmount, - takerAddress, - ), - ).to.be.rejectedWith(ExchangeContractErrs.InsufficientRemainingFillAmount); - }); - }); - describe('validateCancelOrderAndThrowIfInvalidAsync', () => { - let signedOrder: SignedOrder; - const cancelAmount = new BigNumber(3); - beforeEach(async () => { - [coinbase, makerAddress, takerAddress] = userAddresses; - const [makerToken, takerToken] = tokenUtils.getDummyTokens(); - makerTokenAddress = makerToken.address; - takerTokenAddress = takerToken.address; - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - }); - it('should throw when cancel amount is zero', async () => { - const zeroCancelAmount = new BigNumber(0); - return expect( - contractWrappers.exchange.validateCancelOrderThrowIfInvalidAsync(signedOrder, zeroCancelAmount), - ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); - }); - 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, - ); - return expect( - contractWrappers.exchange.validateCancelOrderThrowIfInvalidAsync(expiredSignedOrder, cancelAmount), - ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelExpired); - }); - it('should throw when order is already cancelled or filled', async () => { - await contractWrappers.exchange.cancelOrderAsync(signedOrder, fillableAmount); - return expect( - contractWrappers.exchange.validateCancelOrderThrowIfInvalidAsync(signedOrder, fillableAmount), - ).to.be.rejectedWith(ExchangeContractErrs.OrderAlreadyCancelledOrFilled); - }); - }); - describe('#validateFillOrderBalancesAllowancesThrowIfInvalidAsync', () => { - let exchangeTransferSimulator: ExchangeTransferSimulator; - let transferFromAsync: Sinon.SinonSpy; - const bigNumberMatch = (expected: BigNumber) => { - return Sinon.match((value: BigNumber) => value.eq(expected)); - }; - beforeEach('create exchangeTransferSimulator', async () => { - const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( - contractWrappers.token, - BlockParamLiteral.Latest, - ); - exchangeTransferSimulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore); - transferFromAsync = Sinon.spy(); - exchangeTransferSimulator.transferFromAsync = transferFromAsync as any; - }); - it('should call exchangeTransferSimulator.transferFrom in a correct order', async () => { - const makerFee = new BigNumber(2); - const takerFee = new BigNumber(2); - const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - makerTokenAddress, - takerTokenAddress, - makerFee, - takerFee, - makerAddress, - takerAddress, - fillableAmount, - feeRecipient, - ); - await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( - exchangeTransferSimulator, - signedOrder, - fillableAmount, - takerAddress, - zrxTokenAddress, - ); - // tslint:disable-next-line:custom-no-magic-numbers - expect(transferFromAsync.callCount).to.be.equal(4); - expect( - transferFromAsync - .getCall(0) - .calledWith( - makerTokenAddress, - makerAddress, - takerAddress, - bigNumberMatch(fillableAmount), - TradeSide.Maker, - TransferType.Trade, - ), - ).to.be.true(); - expect( - transferFromAsync - .getCall(1) - .calledWith( - takerTokenAddress, - takerAddress, - makerAddress, - bigNumberMatch(fillableAmount), - TradeSide.Taker, - TransferType.Trade, - ), - ).to.be.true(); - expect( - transferFromAsync - .getCall(2) - .calledWith( - zrxTokenAddress, - makerAddress, - feeRecipient, - bigNumberMatch(makerFee), - TradeSide.Maker, - TransferType.Fee, - ), - ).to.be.true(); - expect( - transferFromAsync - .getCall(3) - .calledWith( - zrxTokenAddress, - takerAddress, - feeRecipient, - bigNumberMatch(takerFee), - TradeSide.Taker, - TransferType.Fee, - ), - ).to.be.true(); - }); - it('should call exchangeTransferSimulator.transferFrom with correct values for an open order', async () => { - const makerFee = new BigNumber(2); - const takerFee = new BigNumber(2); - const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - makerTokenAddress, - takerTokenAddress, - makerFee, - takerFee, - makerAddress, - constants.NULL_ADDRESS, - fillableAmount, - feeRecipient, - ); - await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( - exchangeTransferSimulator, - signedOrder, - fillableAmount, - takerAddress, - zrxTokenAddress, - ); - // tslint:disable-next-line:custom-no-magic-numbers - expect(transferFromAsync.callCount).to.be.equal(4); - expect( - transferFromAsync - .getCall(0) - .calledWith( - makerTokenAddress, - makerAddress, - takerAddress, - bigNumberMatch(fillableAmount), - TradeSide.Maker, - TransferType.Trade, - ), - ).to.be.true(); - expect( - transferFromAsync - .getCall(1) - .calledWith( - takerTokenAddress, - takerAddress, - makerAddress, - bigNumberMatch(fillableAmount), - TradeSide.Taker, - TransferType.Trade, - ), - ).to.be.true(); - expect( - transferFromAsync - .getCall(2) - .calledWith( - zrxTokenAddress, - makerAddress, - feeRecipient, - bigNumberMatch(makerFee), - TradeSide.Maker, - TransferType.Fee, - ), - ).to.be.true(); - expect( - transferFromAsync - .getCall(3) - .calledWith( - zrxTokenAddress, - takerAddress, - feeRecipient, - bigNumberMatch(takerFee), - TradeSide.Taker, - TransferType.Fee, - ), - ).to.be.true(); - }); - it('should correctly round the fillMakerTokenAmount', async () => { - const makerTokenAmount = new BigNumber(3); - const takerTokenAmount = new BigNumber(1); - const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - makerTokenAmount, - takerTokenAmount, - ); - await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( - exchangeTransferSimulator, - signedOrder, - takerTokenAmount, - takerAddress, - zrxTokenAddress, - ); - // tslint:disable-next-line:custom-no-magic-numbers - expect(transferFromAsync.callCount).to.be.equal(4); - const makerFillAmount = transferFromAsync.getCall(0).args[3]; - expect(makerFillAmount).to.be.bignumber.equal(makerTokenAmount); - }); - it('should correctly round the makerFeeAmount', async () => { - const makerFee = new BigNumber(2); - const takerFee = new BigNumber(4); - const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - makerTokenAddress, - takerTokenAddress, - makerFee, - takerFee, - makerAddress, - takerAddress, - fillableAmount, - constants.NULL_ADDRESS, - ); - const fillTakerTokenAmount = fillableAmount.div(2).round(0); - await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( - exchangeTransferSimulator, - signedOrder, - fillTakerTokenAmount, - takerAddress, - zrxTokenAddress, - ); - const makerPartialFee = makerFee.div(2); - const takerPartialFee = takerFee.div(2); - // tslint:disable-next-line:custom-no-magic-numbers - expect(transferFromAsync.callCount).to.be.equal(4); - const partialMakerFee = transferFromAsync.getCall(2).args[3]; - expect(partialMakerFee).to.be.bignumber.equal(makerPartialFee); - const partialTakerFee = transferFromAsync.getCall(3).args[3]; - expect(partialTakerFee).to.be.bignumber.equal(takerPartialFee); - }); - }); -}); // tslint:disable-line:max-file-line-count diff --git a/packages/contract-wrappers/test/token_registry_wrapper_test.ts b/packages/contract-wrappers/test/token_registry_wrapper_test.ts deleted file mode 100644 index 6576d789d..000000000 --- a/packages/contract-wrappers/test/token_registry_wrapper_test.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { BlockchainLifecycle } from '@0xproject/dev-utils'; -import { schemas, SchemaValidator } from '@0xproject/json-schemas'; -import * as chai from 'chai'; -import * as _ from 'lodash'; -import 'mocha'; - -import { ContractWrappers, Token } from '../src'; - -import { chaiSetup } from './utils/chai_setup'; -import { constants } from './utils/constants'; -import { provider, web3Wrapper } from './utils/web3_wrapper'; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); - -const TOKEN_REGISTRY_SIZE_AFTER_MIGRATION = 7; - -describe('TokenRegistryWrapper', () => { - let contractWrappers: ContractWrappers; - let tokens: Token[]; - const tokenAddressBySymbol: { [symbol: string]: string } = {}; - const tokenAddressByName: { [symbol: string]: string } = {}; - const tokenBySymbol: { [symbol: string]: Token } = {}; - const tokenByName: { [symbol: string]: Token } = {}; - const registeredSymbol = 'ZRX'; - const registeredName = '0x Protocol Token'; - const unregisteredSymbol = 'MAL'; - const unregisteredName = 'Malicious Token'; - const config = { - networkId: constants.TESTRPC_NETWORK_ID, - }; - before(async () => { - contractWrappers = new ContractWrappers(provider, config); - tokens = await contractWrappers.tokenRegistry.getTokensAsync(); - _.map(tokens, token => { - tokenAddressBySymbol[token.symbol] = token.address; - tokenAddressByName[token.name] = token.address; - tokenBySymbol[token.symbol] = token; - tokenByName[token.name] = token; - }); - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - describe('#getTokensAsync', () => { - it('should return all the tokens added to the tokenRegistry during the migration', async () => { - expect(tokens).to.have.lengthOf(TOKEN_REGISTRY_SIZE_AFTER_MIGRATION); - - const schemaValidator = new SchemaValidator(); - _.each(tokens, token => { - const validationResult = schemaValidator.validate(token, schemas.tokenSchema); - expect(validationResult.errors).to.have.lengthOf(0); - }); - }); - }); - describe('#getTokenAddressesAsync', () => { - it('should return all the token addresses added to the tokenRegistry during the migration', async () => { - const tokenAddresses = await contractWrappers.tokenRegistry.getTokenAddressesAsync(); - expect(tokenAddresses).to.have.lengthOf(TOKEN_REGISTRY_SIZE_AFTER_MIGRATION); - - const schemaValidator = new SchemaValidator(); - _.each(tokenAddresses, tokenAddress => { - const validationResult = schemaValidator.validate(tokenAddress, schemas.addressSchema); - expect(validationResult.errors).to.have.lengthOf(0); - expect(tokenAddress).to.not.be.equal(constants.NULL_ADDRESS); - }); - }); - }); - describe('#getTokenAddressBySymbol', () => { - it('should return correct address for a token in the registry', async () => { - const tokenAddress = await contractWrappers.tokenRegistry.getTokenAddressBySymbolIfExistsAsync( - registeredSymbol, - ); - expect(tokenAddress).to.be.equal(tokenAddressBySymbol[registeredSymbol]); - }); - it('should return undefined for a token out of registry', async () => { - const tokenAddress = await contractWrappers.tokenRegistry.getTokenAddressBySymbolIfExistsAsync( - unregisteredSymbol, - ); - expect(tokenAddress).to.be.undefined(); - }); - }); - describe('#getTokenAddressByName', () => { - it('should return correct address for a token in the registry', async () => { - const tokenAddress = await contractWrappers.tokenRegistry.getTokenAddressByNameIfExistsAsync( - registeredName, - ); - expect(tokenAddress).to.be.equal(tokenAddressByName[registeredName]); - }); - it('should return undefined for a token out of registry', async () => { - const tokenAddress = await contractWrappers.tokenRegistry.getTokenAddressByNameIfExistsAsync( - unregisteredName, - ); - expect(tokenAddress).to.be.undefined(); - }); - }); - describe('#getTokenBySymbol', () => { - it('should return correct token for a token in the registry', async () => { - const token = await contractWrappers.tokenRegistry.getTokenBySymbolIfExistsAsync(registeredSymbol); - expect(token).to.be.deep.equal(tokenBySymbol[registeredSymbol]); - }); - it('should return undefined for a token out of registry', async () => { - const token = await contractWrappers.tokenRegistry.getTokenBySymbolIfExistsAsync(unregisteredSymbol); - expect(token).to.be.undefined(); - }); - }); - describe('#getTokenByName', () => { - it('should return correct token for a token in the registry', async () => { - const token = await contractWrappers.tokenRegistry.getTokenByNameIfExistsAsync(registeredName); - expect(token).to.be.deep.equal(tokenByName[registeredName]); - }); - it('should return undefined for a token out of registry', async () => { - const token = await contractWrappers.tokenRegistry.getTokenByNameIfExistsAsync(unregisteredName); - expect(token).to.be.undefined(); - }); - }); - describe('#getTokenIfExistsAsync', () => { - it('should return the token added to the tokenRegistry during the migration', async () => { - const aToken = tokens[0]; - - const token = await contractWrappers.tokenRegistry.getTokenIfExistsAsync(aToken.address); - const schemaValidator = new SchemaValidator(); - const validationResult = schemaValidator.validate(token, schemas.tokenSchema); - expect(validationResult.errors).to.have.lengthOf(0); - }); - it('should return return undefined when passed a token address not in the tokenRegistry', async () => { - const unregisteredTokenAddress = '0x5409ed021d9299bf6814279a6a1411a7e866a631'; - const tokenIfExists = await contractWrappers.tokenRegistry.getTokenIfExistsAsync(unregisteredTokenAddress); - expect(tokenIfExists).to.be.undefined(); - }); - }); -}); -- cgit v1.2.3