From dd4541c82576a59e85484a55768c990f9a4e59ce Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Sat, 10 Aug 2019 12:01:30 +0200 Subject: [PATCH] Fill Order Assertion Wrapper --- .../assertion_wrappers/fill_order_wrapper.ts | 282 +++++++ .../test/balance_stores/balance_store.ts | 79 ++ .../blockchain_balance_store.ts | 39 + .../balance_stores/local_balance_store.ts | 106 +++ contracts/exchange/test/core.ts | 791 +----------------- contracts/test-utils/src/index.ts | 3 + contracts/test-utils/src/types.ts | 35 + 7 files changed, 579 insertions(+), 756 deletions(-) create mode 100644 contracts/exchange/test/assertion_wrappers/fill_order_wrapper.ts create mode 100644 contracts/exchange/test/balance_stores/balance_store.ts create mode 100644 contracts/exchange/test/balance_stores/blockchain_balance_store.ts create mode 100644 contracts/exchange/test/balance_stores/local_balance_store.ts diff --git a/contracts/exchange/test/assertion_wrappers/fill_order_wrapper.ts b/contracts/exchange/test/assertion_wrappers/fill_order_wrapper.ts new file mode 100644 index 0000000000..5aceb04da8 --- /dev/null +++ b/contracts/exchange/test/assertion_wrappers/fill_order_wrapper.ts @@ -0,0 +1,282 @@ +import { artifacts as proxyArtifacts , ERC1155ProxyWrapper, ERC20Wrapper, ERC721Wrapper } from '@0x/contracts-asset-proxy'; +import { artifacts as erc20Artifacts } from '@0x/contracts-erc20'; +import { artifacts as erc721Artifacts } from '@0x/contracts-erc721'; +import { + chaiSetup, + FillEventArgs, + LogDecoder, + OrderStatus, + orderUtils, + Web3ProviderEngine, +} from '@0x/contracts-test-utils'; +import { orderHashUtils } from '@0x/order-utils'; +import { FillResults, SignedOrder } from '@0x/types'; +import { BigNumber } from '@0x/utils'; +import { Web3Wrapper } from '@0x/web3-wrapper'; +import * as chai from 'chai'; +import { LogWithDecodedArgs, TransactionReceiptWithDecodedLogs, ZeroExProvider } from 'ethereum-types'; +import * as _ from 'lodash'; + +import { artifacts, ExchangeContract } from '../../src'; +import { BalanceStore } from '../balance_stores/balance_store'; +import { BlockchainBalanceStore } from '../balance_stores/blockchain_balance_store'; +import { LocalBalanceStore } from '../balance_stores/local_balance_store'; + +chaiSetup.configure(); +const expect = chai.expect; + +export class FillOrderWrapper { + private readonly _exchange: ExchangeContract; + private readonly _blockchainBalanceStore: BlockchainBalanceStore; + private readonly _web3Wrapper: Web3Wrapper; + /** + * Simulates matching two orders by transferring amounts defined in + * `transferAmounts` and returns the results. + * @param orders The orders being matched and their filled states. + * @param takerAddress Address of taker (the address who matched the two orders) + * @param tokenBalances Current token balances. + * @param transferAmounts Amounts to transfer during the simulation. + * @return The new account balances and fill events that occurred during the match. + */ + public static simulateFillOrder( + signedOrder: SignedOrder, + takerAddress: string, + opts: { takerAssetFillAmount?: BigNumber } = {}, + initBalanceStore: BalanceStore, + partialFillResults: Partial, + ): [FillResults, FillEventArgs, BalanceStore] { + const balanceStore = LocalBalanceStore.create(initBalanceStore); + const takerAssetFillAmount = + opts.takerAssetFillAmount !== undefined ? opts.takerAssetFillAmount : signedOrder.takerAssetAmount; + const fillResults = FillOrderWrapper._createFillResultsFromPartial( + partialFillResults, + signedOrder, + takerAssetFillAmount, + ); + const fillEvent = FillOrderWrapper.simulateFillEvent(signedOrder, takerAddress, fillResults); + // Taker -> Maker + balanceStore.transferAsset( + takerAddress, + signedOrder.makerAddress, + fillResults.takerAssetFilledAmount, + signedOrder.takerAssetData, + ); + // Taker -> Fee Recipient + balanceStore.transferAsset( + takerAddress, + signedOrder.feeRecipientAddress, + fillResults.takerFeePaid, + signedOrder.takerFeeAssetData, + ); + // Maker -> Taker + balanceStore.transferAsset( + signedOrder.makerAddress, + takerAddress, + fillResults.makerAssetFilledAmount, + signedOrder.makerAssetData, + ); + // Maker -> Fee Recipient + balanceStore.transferAsset( + signedOrder.makerAddress, + signedOrder.feeRecipientAddress, + fillResults.makerFeePaid, + signedOrder.makerFeeAssetData, + ); + return [fillResults, fillEvent, balanceStore]; + } + /** + * Simulates the event emitted by the exchange contract when an order is filled. + */ + public static simulateFillEvent(order: SignedOrder, takerAddress: string, fillResults: FillResults): FillEventArgs { + // prettier-ignore + return { + orderHash: orderHashUtils.getOrderHashHex(order), + makerAddress: order.makerAddress, + takerAddress, + makerAssetFilledAmount: fillResults.makerAssetFilledAmount, + takerAssetFilledAmount: fillResults.takerAssetFilledAmount, + makerFeePaid: fillResults.makerFeePaid, + takerFeePaid: fillResults.takerFeePaid, + }; + } + /** + * Extract the exchanges `Fill` event from a transaction receipt. + */ + public static _extractFillEventsfromReceipt(receipt: TransactionReceiptWithDecodedLogs): FillEventArgs[] { + interface RawFillEventArgs { + orderHash: string; + makerAddress: string; + takerAddress: string; + makerAssetFilledAmount: string; + takerAssetFilledAmount: string; + makerFeePaid: string; + takerFeePaid: string; + } + const actualFills = (_.filter(receipt.logs, ['event', 'Fill']) as any) as Array< + LogWithDecodedArgs + >; + // Convert RawFillEventArgs to FillEventArgs. + return actualFills.map(fill => ({ + orderHash: fill.args.orderHash, + makerAddress: fill.args.makerAddress, + takerAddress: fill.args.takerAddress, + makerAssetFilledAmount: new BigNumber(fill.args.makerAssetFilledAmount), + takerAssetFilledAmount: new BigNumber(fill.args.takerAssetFilledAmount), + makerFeePaid: new BigNumber(fill.args.makerFeePaid), + takerFeePaid: new BigNumber(fill.args.takerFeePaid), + })); + } + /** + * Returns a completed `FillResults` given a partial instance. The correct values are inferred by the + * values in the `signedOrder` and `takerAssetFillAmount`. + */ + private static _createFillResultsFromPartial( + partialFillResults: Partial, + signedOrder: SignedOrder, + takerAssetFillAmount: BigNumber, + ): FillResults { + return { + makerAssetFilledAmount: + partialFillResults.makerAssetFilledAmount !== undefined + ? partialFillResults.makerAssetFilledAmount + : signedOrder.makerAssetAmount.times(takerAssetFillAmount).dividedBy(signedOrder.takerAssetAmount), + takerAssetFilledAmount: + partialFillResults.takerAssetFilledAmount !== undefined + ? partialFillResults.takerAssetFilledAmount + : signedOrder.takerAssetAmount.times(takerAssetFillAmount).dividedBy(signedOrder.takerAssetAmount), + makerFeePaid: + partialFillResults.makerFeePaid !== undefined + ? partialFillResults.makerFeePaid + : signedOrder.makerFee.times(takerAssetFillAmount).dividedBy(signedOrder.takerAssetAmount), + takerFeePaid: + partialFillResults.takerFeePaid !== undefined + ? partialFillResults.takerFeePaid + : signedOrder.takerFee.times(takerAssetFillAmount).dividedBy(signedOrder.takerAssetAmount), + }; + } + /** + * Constructor. + * @param exchangeContract Insstance of the deployed exchange contract + * @param erc20Wrapper The ERC20 Wrapper used to interface with deployed erc20 tokens. + * @param erc721Wrapper The ERC721 Wrapper used to interface with deployed erc20 tokens. + * @param erc1155ProxyWrapper The ERC1155 Proxy Wrapper used to interface with deployed erc20 tokens. + * @param provider Web3 provider to be used by a `Web3Wrapper` instance + */ + public constructor( + exchangeContract: ExchangeContract, + erc20Wrapper: ERC20Wrapper, + erc721Wrapper: ERC721Wrapper, + erc1155ProxyWrapper: ERC1155ProxyWrapper, + provider: Web3ProviderEngine | ZeroExProvider, + ) { + this._exchange = exchangeContract; + this._blockchainBalanceStore = new BlockchainBalanceStore(erc20Wrapper, erc721Wrapper, erc1155ProxyWrapper); + this._web3Wrapper = new Web3Wrapper(provider); + } + /** + * Returns the balance store used by this wrapper. + */ + public getBlockchainBalanceStore(): BlockchainBalanceStore { + return this._blockchainBalanceStore; + } + /** + * Fills an order and asserts the effects. This includes + * 1. The order info (via `getOrderInfo`) + * 2. The fill results returned by making an `eth_call` to `exchange.fillOrder` + * 3. The events emitted by the exchange when the order is filled. + * 4. The balance changes as a result of filling the order. + */ + public async fillOrderAndAssertEffectsAsync( + signedOrder: SignedOrder, + from: string, + opts: { takerAssetFillAmount?: BigNumber } = {}, + expectedFillResults: Partial = {}, + ): Promise { + // Get init state + await this._blockchainBalanceStore.updateBalancesAsync(); + const initTakerAssetFilledAmount = await this._exchange.filled.callAsync( + orderHashUtils.getOrderHashHex(signedOrder), + ); + // Assert init state of exchange + await this._assertOrderStateAsync(signedOrder, initTakerAssetFilledAmount); + // Simulate and execute fill then assert outputs + const [ + simulatedFillResults, + simulatedFillEvent, + simulatedFinalBalanceStore, + ] = FillOrderWrapper.simulateFillOrder( + signedOrder, + from, + opts, + this._blockchainBalanceStore, + expectedFillResults, + ); + const [fillResults, fillEvent] = await this._fillOrderAsync(signedOrder, from, opts); + // Assert state transition + expect(simulatedFillResults, 'Fill Results').to.be.deep.equal(fillResults); + expect(simulatedFillEvent, 'Fill Events').to.be.deep.equal(fillEvent); + const areBalancesEqual = BalanceStore.isEqual(simulatedFinalBalanceStore, this._blockchainBalanceStore); + expect(areBalancesEqual, 'Balances After Fill').to.be.true(); + // Assert end state of exchange + const finalTakerAssetFilledAmount = initTakerAssetFilledAmount.plus(fillResults.takerAssetFilledAmount); + await this._assertOrderStateAsync(signedOrder, finalTakerAssetFilledAmount); + } + /** + * Fills an order on-chain. As an optimization this function auto-updates the blockchain balance store + * used by this contract. + */ + protected async _fillOrderAsync( + signedOrder: SignedOrder, + from: string, + opts: { takerAssetFillAmount?: BigNumber } = {}, + ): Promise<[FillResults, FillEventArgs]> { + const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount); + const fillResults = await this._exchange.fillOrder.callAsync( + params.order, + params.takerAssetFillAmount, + params.signature, + { from }, + ); + // @TODO: Replace with `awaitTransactionAsync` once `development` is merged into `3.0` branch + const txHash = await this._exchange.fillOrder.sendTransactionAsync( + params.order, + params.takerAssetFillAmount, + params.signature, + { from }, + ); + const logDecoder = new LogDecoder(this._web3Wrapper, { + ...artifacts, + ...proxyArtifacts, + ...erc20Artifacts, + ...erc721Artifacts, + }); + const txReceipt = await logDecoder.getTxWithDecodedLogsAsync(txHash); + const fillEvent = FillOrderWrapper._extractFillEventsfromReceipt(txReceipt)[0]; + await this._blockchainBalanceStore.updateBalancesAsync(); + return [fillResults, fillEvent]; + } + /** + * Asserts that the provided order's fill amount and order status + * are the expected values. + * @param order The order to verify for a correct state. + * @param expectedFilledAmount The amount that the order should + * have been filled. + * @param side The side that the provided order should be matched on. + * @param exchangeWrapper The ExchangeWrapper instance. + */ + private async _assertOrderStateAsync( + order: SignedOrder, + expectedFilledAmount: BigNumber = new BigNumber(0), + ): Promise { + const orderInfo = await this._exchange.getOrderInfo.callAsync(order); + // Check filled amount of order. + const actualFilledAmount = orderInfo.orderTakerAssetFilledAmount; + expect(actualFilledAmount, 'order filled amount').to.be.bignumber.equal(expectedFilledAmount); + // Check status of order. + const expectedStatus = expectedFilledAmount.isGreaterThanOrEqualTo(order.takerAssetAmount) + ? OrderStatus.FullyFilled + : OrderStatus.Fillable; + const actualStatus = orderInfo.orderStatus; + expect(actualStatus, 'order status').to.equal(expectedStatus); + } +} diff --git a/contracts/exchange/test/balance_stores/balance_store.ts b/contracts/exchange/test/balance_stores/balance_store.ts new file mode 100644 index 0000000000..7d37f38f9a --- /dev/null +++ b/contracts/exchange/test/balance_stores/balance_store.ts @@ -0,0 +1,79 @@ +import { ERC1155Holdings, ERC1155HoldingsByOwner, TokenBalances } from '@0x/contracts-test-utils'; +import { BigNumber } from '@0x/utils'; +import * as _ from 'lodash'; + +export class BalanceStore { + protected _balances: TokenBalances; + /** + * Returns true iff balance stores do have the same entries. + * @param lhs First balance store to compare + * @param rhs Second balance store to compare + */ + public static isEqual(lhs: BalanceStore, rhs: BalanceStore): boolean { + return _.isEqual(lhs.getRawBalances(), rhs.getRawBalances()); + } + /** + * Throws iff balance stores do not have the same entries. + * @param lhs First balance store to compare + * @param rhs Second balance store to compare + */ + public static assertEqual(lhs: BalanceStore, rhs: BalanceStore): void { + if (!BalanceStore.isEqual(lhs, rhs)) { + throw new Error(`Balance stores are not equal:\n\nLeft:\n${lhs}\n\nRight:\n${rhs}`); + } + } + /** + * Restructures `ERC1155HoldingsByOwner` to be compatible with `TokenBalances.erc1155`. + * @param erc1155HoldingsByOwner Holdings returned by `ERC1155ProxyWrapper.getBalancesAsync()`. + */ + protected static _transformERC1155Holdings(erc1155HoldingsByOwner: ERC1155HoldingsByOwner): ERC1155Holdings { + const result = {}; + for (const owner of _.keys(erc1155HoldingsByOwner.fungible)) { + for (const contract of _.keys(erc1155HoldingsByOwner.fungible[owner])) { + _.set(result as any, [owner, contract, 'fungible'], erc1155HoldingsByOwner.fungible[owner][contract]); + } + } + for (const owner of _.keys(erc1155HoldingsByOwner.nonFungible)) { + for (const contract of _.keys(erc1155HoldingsByOwner.nonFungible[owner])) { + const tokenIds = _.flatten(_.values(erc1155HoldingsByOwner.nonFungible[owner][contract])); + _.set(result as any, [owner, contract, 'nonFungible'], _.uniqBy(tokenIds, v => v.toString(10))); + } + } + return result; + } + /** + * Encodes token balances in a way that they can be compared by lodash. + */ + protected static _encodeTokenBalances(obj: any): any { + if (!_.isPlainObject(obj)) { + if (BigNumber.isBigNumber(obj)) { + return obj.toString(10); + } + if (_.isArray(obj)) { + return _.sortBy(obj, v => BalanceStore._encodeTokenBalances(v)); + } + return obj; + } + const keys = _.keys(obj).sort(); + return _.zip(keys, keys.map(k => BalanceStore._encodeTokenBalances(obj[k]))); + } + /** + * Constructor. + */ + public constructor() { + this._balances = { erc20: {}, erc721: {}, erc1155: {} }; + } + /** + * Copies the balance from an existing balance store. + * @param balanceStore to copy balances from. + */ + public copyBalancesFrom(balanceStore: BalanceStore): void { + this._balances = _.cloneDeep(balanceStore._balances); + } + /** + * Returns the raw `TokenBalances` that this class encapsulates. + */ + public getRawBalances(): TokenBalances { + return _.cloneDeep(this._balances); + } +} diff --git a/contracts/exchange/test/balance_stores/blockchain_balance_store.ts b/contracts/exchange/test/balance_stores/blockchain_balance_store.ts new file mode 100644 index 0000000000..a377212385 --- /dev/null +++ b/contracts/exchange/test/balance_stores/blockchain_balance_store.ts @@ -0,0 +1,39 @@ +import { ERC1155ProxyWrapper, ERC20Wrapper, ERC721Wrapper } from '@0x/contracts-asset-proxy'; +import * as _ from 'lodash'; + +import { BalanceStore } from './balance_store'; + +export class BlockchainBalanceStore extends BalanceStore { + private readonly _erc20Wrapper: ERC20Wrapper; + private readonly _erc721Wrapper: ERC721Wrapper; + private readonly _erc1155ProxyWrapper: ERC1155ProxyWrapper; + /** + * Constructor. + * @param erc20Wrapper The ERC20 Wrapper used to interface with deployed erc20 tokens. + * @param erc721Wrapper The ERC721 Wrapper used to interface with deployed erc20 tokens. + * @param erc1155ProxyWrapper The ERC1155 Proxy Wrapper used to interface with deployed erc20 tokens. + */ + public constructor( + erc20Wrapper: ERC20Wrapper, + erc721Wrapper: ERC721Wrapper, + erc1155ProxyWrapper: ERC1155ProxyWrapper, + ) { + super(); + this._erc20Wrapper = erc20Wrapper; + this._erc721Wrapper = erc721Wrapper; + this._erc1155ProxyWrapper = erc1155ProxyWrapper; + } + /** + * Updates balances by querying on-chain values managed by the erc20, erc721, and erc1155 wrappers. + */ + public async updateBalancesAsync(): Promise { + const [erc20, erc721, erc1155] = await Promise.all([ + this._erc20Wrapper.getBalancesAsync(), + this._erc721Wrapper.getBalancesAsync(), + this._erc1155ProxyWrapper.getBalancesAsync(), + ]); + this._balances.erc20 = erc20; + this._balances.erc721 = erc721; + this._balances.erc1155 = BalanceStore._transformERC1155Holdings(erc1155); + } +} diff --git a/contracts/exchange/test/balance_stores/local_balance_store.ts b/contracts/exchange/test/balance_stores/local_balance_store.ts new file mode 100644 index 0000000000..87e41e9164 --- /dev/null +++ b/contracts/exchange/test/balance_stores/local_balance_store.ts @@ -0,0 +1,106 @@ +import { assetDataUtils } from '@0x/order-utils'; +import { AssetProxyId } from '@0x/types'; +import { BigNumber } from '@0x/utils'; +import * as _ from 'lodash'; + +import { BalanceStore } from './balance_store'; + +export class LocalBalanceStore extends BalanceStore { + /** + * Creates a new balance store based on an existing one. + * @param balanceStore Existing balance store whose values should be copied. + */ + public static create(sourceBalanceStore?: BalanceStore): LocalBalanceStore { + const localBalanceStore = new LocalBalanceStore(); + if (sourceBalanceStore !== undefined) { + localBalanceStore.copyBalancesFrom(sourceBalanceStore); + } + return localBalanceStore; + } + /** + * Constructor. + */ + public constructor() { + super(); + } + /** + * Transfers assets from `fromAddress` to `toAddress`. + * @param fromAddress Sender of asset(s) + * @param toAddress Receiver of asset(s) + * @param amount Amount of asset(s) to transfer + * @param assetData Asset data of assets being transferred. + */ + public transferAsset(fromAddress: string, toAddress: string, amount: BigNumber, assetData: string): void { + if (fromAddress === toAddress) { + return; + } + const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); + switch (assetProxyId) { + case AssetProxyId.ERC20: { + const erc20AssetData = assetDataUtils.decodeERC20AssetData(assetData); + const assetAddress = erc20AssetData.tokenAddress; + const fromBalances = this._balances.erc20[fromAddress]; + const toBalances = this._balances.erc20[toAddress]; + fromBalances[assetAddress] = fromBalances[assetAddress].minus(amount); + toBalances[assetAddress] = toBalances[assetAddress].plus(amount); + break; + } + case AssetProxyId.ERC721: { + const erc721AssetData = assetDataUtils.decodeERC721AssetData(assetData); + const assetAddress = erc721AssetData.tokenAddress; + const tokenId = erc721AssetData.tokenId; + const fromTokens = this._balances.erc721[fromAddress][assetAddress]; + const toTokens = this._balances.erc721[toAddress][assetAddress]; + if (amount.gte(1)) { + const tokenIndex = _.findIndex(fromTokens, t => t.eq(tokenId)); + if (tokenIndex !== -1) { + fromTokens.splice(tokenIndex, 1); + toTokens.push(tokenId); + } + } + break; + } + case AssetProxyId.ERC1155: { + const erc1155AssetData = assetDataUtils.decodeERC1155AssetData(assetData); + const assetAddress = erc1155AssetData.tokenAddress; + const fromBalances = this._balances.erc1155[fromAddress][assetAddress]; + const toBalances = this._balances.erc1155[toAddress][assetAddress]; + for (const i of _.times(erc1155AssetData.tokenIds.length)) { + const tokenId = erc1155AssetData.tokenIds[i]; + const tokenValue = erc1155AssetData.tokenValues[i]; + const tokenAmount = amount.times(tokenValue); + if (tokenAmount.gt(0)) { + const tokenIndex = _.findIndex(fromBalances.nonFungible, t => t.eq(tokenId)); + if (tokenIndex !== -1) { + // Transfer a non-fungible. + fromBalances.nonFungible.splice(tokenIndex, 1); + toBalances.nonFungible.push(tokenId); + } else { + // Transfer a fungible. + const _tokenId = tokenId.toString(10); + fromBalances.fungible[_tokenId] = fromBalances.fungible[_tokenId].minus(tokenAmount); + toBalances.fungible[_tokenId] = toBalances.fungible[_tokenId].plus(tokenAmount); + } + } + } + // sort NFT's by name + toBalances.nonFungible = _.sortBy(toBalances.nonFungible); + break; + } + case AssetProxyId.MultiAsset: { + const multiAssetData = assetDataUtils.decodeMultiAssetData(assetData); + for (const i of _.times(multiAssetData.amounts.length)) { + const nestedAmount = amount.times(multiAssetData.amounts[i]); + const nestedAssetData = multiAssetData.nestedAssetData[i]; + this.transferAsset(fromAddress, toAddress, nestedAmount, nestedAssetData); + } + break; + } + case AssetProxyId.StaticCall: + // Do nothing + break; + default: + throw new Error(`Unhandled asset proxy ID: ${assetProxyId}`); + } + } +} diff --git a/contracts/exchange/test/core.ts b/contracts/exchange/test/core.ts index d68a3d89ad..bef3e53911 100644 --- a/contracts/exchange/test/core.ts +++ b/contracts/exchange/test/core.ts @@ -13,7 +13,6 @@ import { ERC1155MintableContract } from '@0x/contracts-erc1155'; import { artifacts as erc20Artifacts, DummyERC20TokenContract, - DummyERC20TokenTransferEventArgs, DummyNoReturnERC20TokenContract, } from '@0x/contracts-erc20'; import { DummyERC721TokenContract } from '@0x/contracts-erc721'; @@ -56,6 +55,8 @@ chaiSetup.configure(); const expect = chai.expect; const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); +import { FillOrderWrapper } from './assertion_wrappers/fill_order_wrapper'; + // tslint:disable:no-unnecessary-type-assertion describe('Exchange core', () => { let chainId: number; @@ -99,6 +100,8 @@ describe('Exchange core', () => { let defaultTakerAssetAddress: string; let defaultFeeAssetAddress: string; + let fillOrderWrapper: FillOrderWrapper; + before(async () => { await blockchainLifecycle.startAsync(); }); @@ -132,6 +135,16 @@ describe('Exchange core', () => { numDummyErc20ToDeploy, constants.DUMMY_TOKEN_DECIMALS, ); + noReturnErc20Token = await DummyNoReturnERC20TokenContract.deployFrom0xArtifactAsync( + erc20Artifacts.DummyNoReturnERC20Token, + provider, + txDefaults, + constants.DUMMY_TOKEN_NAME, + constants.DUMMY_TOKEN_SYMBOL, + constants.DUMMY_TOKEN_DECIMALS, + constants.DUMMY_TOKEN_TOTAL_SUPPLY, + ); + erc20Wrapper.addDummyTokenContract(noReturnErc20Token); [erc721Token] = await erc721Wrapper.deployDummyTokensAsync(); erc1155Proxy = await erc1155ProxyWrapper.deployProxyAsync(); [erc1155Wrapper] = await erc1155ProxyWrapper.deployDummyContractsAsync(); @@ -225,6 +238,7 @@ describe('Exchange core', () => { }; const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)]; orderFactory = new OrderFactory(privateKey, defaultOrderParams); + fillOrderWrapper = new FillOrderWrapper(exchange, erc20Wrapper, erc721Wrapper, erc1155ProxyWrapper, provider); }); beforeEach(async () => { await blockchainLifecycle.startAsync(); @@ -390,37 +404,7 @@ describe('Exchange core', () => { }); it('should not emit transfer events for transfers where from == to', async () => { - const txReceipt = await exchangeWrapper.fillOrderAsync(signedOrder, makerAddress); - const logs = txReceipt.logs; - const transferLogs = _.filter( - logs, - log => (log as LogWithDecodedArgs).event === 'Transfer', - ); - expect(transferLogs.length).to.be.equal(2); - expect((transferLogs[0] as LogWithDecodedArgs).address).to.be.equal( - feeToken.address, - ); - expect((transferLogs[0] as LogWithDecodedArgs).args._from).to.be.equal( - makerAddress, - ); - expect((transferLogs[0] as LogWithDecodedArgs).args._to).to.be.equal( - feeRecipientAddress, - ); - expect( - (transferLogs[0] as LogWithDecodedArgs).args._value, - ).to.be.bignumber.equal(signedOrder.makerFee); - expect((transferLogs[1] as LogWithDecodedArgs).address).to.be.equal( - feeToken.address, - ); - expect((transferLogs[1] as LogWithDecodedArgs).args._from).to.be.equal( - makerAddress, - ); - expect((transferLogs[1] as LogWithDecodedArgs).args._to).to.be.equal( - feeRecipientAddress, - ); - expect( - (transferLogs[1] as LogWithDecodedArgs).args._value, - ).to.be.bignumber.equal(signedOrder.takerFee); + await fillOrderWrapper.fillOrderAndAssertEffectsAsync(signedOrder, makerAddress); }); it('should revert if order is expired', async () => { @@ -476,37 +460,7 @@ describe('Exchange core', () => { await erc20TokenA.setBalance.awaitTransactionSuccessAsync(makerAddress, constants.ZERO_AMOUNT, { from: owner, }); - const txReceipt = await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); - const logs = txReceipt.logs; - const transferLogs = _.filter( - logs, - log => (log as LogWithDecodedArgs).event === 'Transfer', - ); - expect(transferLogs.length).to.be.equal(2); - expect((transferLogs[0] as LogWithDecodedArgs).address).to.be.equal( - erc20TokenA.address, - ); - expect((transferLogs[0] as LogWithDecodedArgs).args._from).to.be.equal( - takerAddress, - ); - expect((transferLogs[0] as LogWithDecodedArgs).args._to).to.be.equal( - makerAddress, - ); - expect( - (transferLogs[0] as LogWithDecodedArgs).args._value, - ).to.be.bignumber.equal(signedOrder.takerAssetAmount); - expect((transferLogs[1] as LogWithDecodedArgs).address).to.be.equal( - erc20TokenA.address, - ); - expect((transferLogs[1] as LogWithDecodedArgs).args._from).to.be.equal( - makerAddress, - ); - expect((transferLogs[1] as LogWithDecodedArgs).args._to).to.be.equal( - takerAddress, - ); - expect( - (transferLogs[1] as LogWithDecodedArgs).args._value, - ).to.be.bignumber.equal(signedOrder.makerAssetAmount); + await fillOrderWrapper.fillOrderAndAssertEffectsAsync(signedOrder, takerAddress); }); it('should allow the taker to pay fees with an asset that received by the maker', async () => { const makerAssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); @@ -519,49 +473,7 @@ describe('Exchange core', () => { await erc20TokenA.setBalance.awaitTransactionSuccessAsync(takerAddress, constants.ZERO_AMOUNT, { from: owner, }); - const txReceipt = await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); - const logs = txReceipt.logs; - const transferLogs = _.filter( - logs, - log => (log as LogWithDecodedArgs).event === 'Transfer', - ); - expect(transferLogs.length).to.be.equal(3); - expect((transferLogs[0] as LogWithDecodedArgs).address).to.be.equal( - erc20TokenB.address, - ); - expect((transferLogs[0] as LogWithDecodedArgs).args._from).to.be.equal( - takerAddress, - ); - expect((transferLogs[0] as LogWithDecodedArgs).args._to).to.be.equal( - makerAddress, - ); - expect( - (transferLogs[0] as LogWithDecodedArgs).args._value, - ).to.be.bignumber.equal(signedOrder.takerAssetAmount); - expect((transferLogs[1] as LogWithDecodedArgs).address).to.be.equal( - erc20TokenA.address, - ); - expect((transferLogs[1] as LogWithDecodedArgs).args._from).to.be.equal( - makerAddress, - ); - expect((transferLogs[1] as LogWithDecodedArgs).args._to).to.be.equal( - takerAddress, - ); - expect( - (transferLogs[1] as LogWithDecodedArgs).args._value, - ).to.be.bignumber.equal(signedOrder.makerAssetAmount); - expect((transferLogs[2] as LogWithDecodedArgs).address).to.be.equal( - erc20TokenA.address, - ); - expect((transferLogs[2] as LogWithDecodedArgs).args._from).to.be.equal( - takerAddress, - ); - expect((transferLogs[2] as LogWithDecodedArgs).args._to).to.be.equal( - feeRecipientAddress, - ); - expect( - (transferLogs[2] as LogWithDecodedArgs).args._value, - ).to.be.bignumber.equal(signedOrder.takerFee); + await fillOrderWrapper.fillOrderAndAssertEffectsAsync(signedOrder, takerAddress); }); it('should allow the maker to pay fees with an asset that received by the taker', async () => { const takerAssetData = assetDataUtils.encodeERC20AssetData(erc20TokenB.address); @@ -574,62 +486,11 @@ describe('Exchange core', () => { await erc20TokenB.setBalance.awaitTransactionSuccessAsync(makerAddress, constants.ZERO_AMOUNT, { from: owner, }); - const txReceipt = await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); - const logs = txReceipt.logs; - const transferLogs = _.filter( - logs, - log => (log as LogWithDecodedArgs).event === 'Transfer', - ); - expect(transferLogs.length).to.be.equal(3); - expect((transferLogs[0] as LogWithDecodedArgs).address).to.be.equal( - erc20TokenB.address, - ); - expect((transferLogs[0] as LogWithDecodedArgs).args._from).to.be.equal( - takerAddress, - ); - expect((transferLogs[0] as LogWithDecodedArgs).args._to).to.be.equal( - makerAddress, - ); - expect( - (transferLogs[0] as LogWithDecodedArgs).args._value, - ).to.be.bignumber.equal(signedOrder.takerAssetAmount); - expect((transferLogs[1] as LogWithDecodedArgs).address).to.be.equal( - erc20TokenA.address, - ); - expect((transferLogs[1] as LogWithDecodedArgs).args._from).to.be.equal( - makerAddress, - ); - expect((transferLogs[1] as LogWithDecodedArgs).args._to).to.be.equal( - takerAddress, - ); - expect( - (transferLogs[1] as LogWithDecodedArgs).args._value, - ).to.be.bignumber.equal(signedOrder.makerAssetAmount); - expect((transferLogs[2] as LogWithDecodedArgs).address).to.be.equal( - erc20TokenB.address, - ); - expect((transferLogs[2] as LogWithDecodedArgs).args._from).to.be.equal( - makerAddress, - ); - expect((transferLogs[2] as LogWithDecodedArgs).args._to).to.be.equal( - feeRecipientAddress, - ); - expect( - (transferLogs[2] as LogWithDecodedArgs).args._value, - ).to.be.bignumber.equal(signedOrder.makerFee); + await fillOrderWrapper.fillOrderAndAssertEffectsAsync(signedOrder, takerAddress); }); }); describe('Testing exchange of ERC20 tokens with no return values', () => { before(async () => { - noReturnErc20Token = await DummyNoReturnERC20TokenContract.deployFrom0xArtifactAsync( - erc20Artifacts.DummyNoReturnERC20Token, - provider, - txDefaults, - constants.DUMMY_TOKEN_NAME, - constants.DUMMY_TOKEN_SYMBOL, - constants.DUMMY_TOKEN_DECIMALS, - constants.DUMMY_TOKEN_TOTAL_SUPPLY, - ); await noReturnErc20Token.setBalance.awaitTransactionSuccessAsync( makerAddress, constants.INITIAL_ERC20_BALANCE, @@ -646,38 +507,7 @@ describe('Exchange core', () => { makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), }); - - const initialMakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(makerAddress); - const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const initialMakerFeeTokenBalance = await feeToken.balanceOf.callAsync(makerAddress); - const initialTakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(takerAddress); - const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - const initialTakerFeeTokenBalance = await feeToken.balanceOf.callAsync(takerAddress); - const initialFeeRecipientFeeTokenBalance = await feeToken.balanceOf.callAsync(feeRecipientAddress); - - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); - - const finalMakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(makerAddress); - const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const finalMakerFeeTokenBalance = await feeToken.balanceOf.callAsync(makerAddress); - const finalTakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(takerAddress); - const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - const finalTakerFeeTokenBalance = await feeToken.balanceOf.callAsync(takerAddress); - const finalFeeRecipientFeeTokenBalance = await feeToken.balanceOf.callAsync(feeRecipientAddress); - - expect(finalMakerBalanceA).to.be.bignumber.equal(initialMakerBalanceA.minus(signedOrder.makerAssetAmount)); - expect(finalMakerBalanceB).to.be.bignumber.equal(initialMakerBalanceB.plus(signedOrder.takerAssetAmount)); - expect(finalTakerBalanceA).to.be.bignumber.equal(initialTakerBalanceA.plus(signedOrder.makerAssetAmount)); - expect(finalTakerBalanceB).to.be.bignumber.equal(initialTakerBalanceB.minus(signedOrder.takerAssetAmount)); - expect(finalMakerFeeTokenBalance).to.be.bignumber.equal( - initialMakerFeeTokenBalance.minus(signedOrder.makerFee), - ); - expect(finalTakerFeeTokenBalance).to.be.bignumber.equal( - initialTakerFeeTokenBalance.minus(signedOrder.takerFee), - ); - expect(finalFeeRecipientFeeTokenBalance).to.be.bignumber.equal( - initialFeeRecipientFeeTokenBalance.plus(signedOrder.makerFee.plus(signedOrder.takerFee)), - ); + await fillOrderWrapper.fillOrderAndAssertEffectsAsync(signedOrder, takerAddress); }); it('should transfer the correct amounts when makerAssetAmount > takerAssetAmount', async () => { signedOrder = await orderFactory.newSignedOrderAsync({ @@ -685,38 +515,7 @@ describe('Exchange core', () => { makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), }); - - const initialMakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(makerAddress); - const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const initialMakerFeeTokenBalance = await feeToken.balanceOf.callAsync(makerAddress); - const initialTakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(takerAddress); - const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - const initialTakerFeeTokenBalance = await feeToken.balanceOf.callAsync(takerAddress); - const initialFeeRecipientFeeTokenBalance = await feeToken.balanceOf.callAsync(feeRecipientAddress); - - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); - - const finalMakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(makerAddress); - const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const finalMakerFeeTokenBalance = await feeToken.balanceOf.callAsync(makerAddress); - const finalTakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(takerAddress); - const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - const finalTakerFeeTokenBalance = await feeToken.balanceOf.callAsync(takerAddress); - const finalFeeRecipientFeeTokenBalance = await feeToken.balanceOf.callAsync(feeRecipientAddress); - - expect(finalMakerBalanceA).to.be.bignumber.equal(initialMakerBalanceA.minus(signedOrder.makerAssetAmount)); - expect(finalMakerBalanceB).to.be.bignumber.equal(initialMakerBalanceB.plus(signedOrder.takerAssetAmount)); - expect(finalTakerBalanceA).to.be.bignumber.equal(initialTakerBalanceA.plus(signedOrder.makerAssetAmount)); - expect(finalTakerBalanceB).to.be.bignumber.equal(initialTakerBalanceB.minus(signedOrder.takerAssetAmount)); - expect(finalMakerFeeTokenBalance).to.be.bignumber.equal( - initialMakerFeeTokenBalance.minus(signedOrder.makerFee), - ); - expect(finalTakerFeeTokenBalance).to.be.bignumber.equal( - initialTakerFeeTokenBalance.minus(signedOrder.takerFee), - ); - expect(finalFeeRecipientFeeTokenBalance).to.be.bignumber.equal( - initialFeeRecipientFeeTokenBalance.plus(signedOrder.makerFee.plus(signedOrder.takerFee)), - ); + await fillOrderWrapper.fillOrderAndAssertEffectsAsync(signedOrder, takerAddress); }); it('should transfer the correct amounts when makerAssetAmount < takerAssetAmount', async () => { signedOrder = await orderFactory.newSignedOrderAsync({ @@ -724,38 +523,7 @@ describe('Exchange core', () => { makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18), }); - - const initialMakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(makerAddress); - const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const initialMakerFeeTokenBalance = await feeToken.balanceOf.callAsync(makerAddress); - const initialTakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(takerAddress); - const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - const initialTakerFeeTokenBalance = await feeToken.balanceOf.callAsync(takerAddress); - const initialFeeRecipientFeeTokenBalance = await feeToken.balanceOf.callAsync(feeRecipientAddress); - - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); - - const finalMakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(makerAddress); - const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const finalMakerFeeTokenBalance = await feeToken.balanceOf.callAsync(makerAddress); - const finalTakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(takerAddress); - const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - const finalTakerFeeTokenBalance = await feeToken.balanceOf.callAsync(takerAddress); - const finalFeeRecipientFeeTokenBalance = await feeToken.balanceOf.callAsync(feeRecipientAddress); - - expect(finalMakerBalanceA).to.be.bignumber.equal(initialMakerBalanceA.minus(signedOrder.makerAssetAmount)); - expect(finalMakerBalanceB).to.be.bignumber.equal(initialMakerBalanceB.plus(signedOrder.takerAssetAmount)); - expect(finalTakerBalanceA).to.be.bignumber.equal(initialTakerBalanceA.plus(signedOrder.makerAssetAmount)); - expect(finalTakerBalanceB).to.be.bignumber.equal(initialTakerBalanceB.minus(signedOrder.takerAssetAmount)); - expect(finalMakerFeeTokenBalance).to.be.bignumber.equal( - initialMakerFeeTokenBalance.minus(signedOrder.makerFee), - ); - expect(finalTakerFeeTokenBalance).to.be.bignumber.equal( - initialTakerFeeTokenBalance.minus(signedOrder.takerFee), - ); - expect(finalFeeRecipientFeeTokenBalance).to.be.bignumber.equal( - initialFeeRecipientFeeTokenBalance.plus(signedOrder.makerFee.plus(signedOrder.takerFee)), - ); + await fillOrderWrapper.fillOrderAndAssertEffectsAsync(signedOrder, takerAddress); }); }); @@ -1070,39 +838,7 @@ describe('Exchange core', () => { makerFee: constants.ZERO_AMOUNT, takerFee: constants.ZERO_AMOUNT, }); - - const initialMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress); - const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const initialMakerFeeTokenBalance = await feeToken.balanceOf.callAsync(makerAddress); - const initialTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress); - const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - const initialTakerFeeTokenBalance = await feeToken.balanceOf.callAsync(takerAddress); - - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); - - const finalMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress); - const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const finalMakerFeeTokenBalance = await feeToken.balanceOf.callAsync(makerAddress); - const finalTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress); - const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - const finalTakerFeeTokenBalance = await feeToken.balanceOf.callAsync(takerAddress); - - expect(finalMakerBalanceA).to.be.bignumber.equal( - initialMakerBalanceA.minus(makerAmounts[0].times(makerAssetAmount)), - ); - expect(finalMakerBalanceB).to.be.bignumber.equal( - initialMakerBalanceB.minus(makerAmounts[1].times(makerAssetAmount)), - ); - expect(finalMakerFeeTokenBalance).to.be.bignumber.equal(initialMakerFeeTokenBalance.plus(takerAssetAmount)); - expect(finalTakerBalanceA).to.be.bignumber.equal( - initialTakerBalanceA.plus(makerAmounts[0].times(makerAssetAmount)), - ); - expect(finalTakerBalanceB).to.be.bignumber.equal( - initialTakerBalanceB.plus(makerAmounts[1].times(makerAssetAmount)), - ); - expect(finalTakerFeeTokenBalance).to.be.bignumber.equal( - initialTakerFeeTokenBalance.minus(takerAssetAmount), - ); + await fillOrderWrapper.fillOrderAndAssertEffectsAsync(signedOrder, takerAddress); }); it('should allow multiple assets to be exchanged for multiple assets', async () => { const makerAmounts = [new BigNumber(10), new BigNumber(20)]; @@ -1128,45 +864,7 @@ describe('Exchange core', () => { makerFee: constants.ZERO_AMOUNT, takerFee: constants.ZERO_AMOUNT, }); - - const initialMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress); - const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const initialMakerFeeTokenBalance = await feeToken.balanceOf.callAsync(makerAddress); - const initialTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress); - const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - const initialTakerFeeTokenBalance = await feeToken.balanceOf.callAsync(takerAddress); - const initialOwnerTakerAsset = await erc721Token.ownerOf.callAsync(takerAssetId); - expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress); - - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); - - const finalMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress); - const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const finalMakerFeeTokenBalance = await feeToken.balanceOf.callAsync(makerAddress); - const finalTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress); - const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - const finalTakerFeeTokenBalance = await feeToken.balanceOf.callAsync(takerAddress); - const finalOwnerTakerAsset = await erc721Token.ownerOf.callAsync(takerAssetId); - - expect(finalMakerBalanceA).to.be.bignumber.equal( - initialMakerBalanceA.minus(makerAmounts[0].times(makerAssetAmount)), - ); - expect(finalMakerBalanceB).to.be.bignumber.equal( - initialMakerBalanceB.minus(makerAmounts[1].times(makerAssetAmount)), - ); - expect(finalMakerFeeTokenBalance).to.be.bignumber.equal( - initialMakerFeeTokenBalance.plus(takerAmounts[0].times(takerAssetAmount)), - ); - expect(finalTakerBalanceA).to.be.bignumber.equal( - initialTakerBalanceA.plus(makerAmounts[0].times(makerAssetAmount)), - ); - expect(finalTakerBalanceB).to.be.bignumber.equal( - initialTakerBalanceB.plus(makerAmounts[1].times(makerAssetAmount)), - ); - expect(finalTakerFeeTokenBalance).to.be.bignumber.equal( - initialTakerFeeTokenBalance.minus(takerAmounts[0].times(takerAssetAmount)), - ); - expect(finalOwnerTakerAsset).to.be.equal(makerAddress); + await fillOrderWrapper.fillOrderAndAssertEffectsAsync(signedOrder, takerAddress); }); it('should allow an order selling multiple assets to be partially filled', async () => { const makerAmounts = [new BigNumber(10), new BigNumber(20)]; @@ -1187,63 +885,8 @@ describe('Exchange core', () => { takerFee: constants.ZERO_AMOUNT, }); - const initialMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress); - const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const initialMakerFeeTokenBalance = await feeToken.balanceOf.callAsync(makerAddress); - const initialTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress); - const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - const initialTakerFeeTokenBalance = await feeToken.balanceOf.callAsync(takerAddress); - const takerAssetFillAmount = takerAssetAmount.dividedToIntegerBy(2); - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { - takerAssetFillAmount, - }); - - const finalMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress); - const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const finalMakerFeeTokenBalance = await feeToken.balanceOf.callAsync(makerAddress); - const finalTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress); - const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - const finalTakerFeeTokenBalance = await feeToken.balanceOf.callAsync(takerAddress); - - expect(finalMakerBalanceA).to.be.bignumber.equal( - initialMakerBalanceA.minus( - makerAmounts[0].times( - makerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), - ), - ), - ); - expect(finalMakerBalanceB).to.be.bignumber.equal( - initialMakerBalanceB.minus( - makerAmounts[1].times( - makerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), - ), - ), - ); - expect(finalMakerFeeTokenBalance).to.be.bignumber.equal( - initialMakerFeeTokenBalance.plus( - takerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), - ), - ); - expect(finalTakerBalanceA).to.be.bignumber.equal( - initialTakerBalanceA.plus( - makerAmounts[0].times( - makerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), - ), - ), - ); - expect(finalTakerBalanceB).to.be.bignumber.equal( - initialTakerBalanceB.plus( - makerAmounts[1].times( - makerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), - ), - ), - ); - expect(finalTakerFeeTokenBalance).to.be.bignumber.equal( - initialTakerFeeTokenBalance.minus( - takerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), - ), - ); + await fillOrderWrapper.fillOrderAndAssertEffectsAsync(signedOrder, takerAddress, { takerAssetFillAmount }); }); it('should allow an order buying multiple assets to be partially filled', async () => { const takerAmounts = [new BigNumber(10), new BigNumber(20)]; @@ -1263,83 +906,21 @@ describe('Exchange core', () => { makerFee: constants.ZERO_AMOUNT, takerFee: constants.ZERO_AMOUNT, }); - - const initialMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress); - const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const initialMakerFeeTokenBalance = await feeToken.balanceOf.callAsync(makerAddress); - const initialTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress); - const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - const initialTakerFeeTokenBalance = await feeToken.balanceOf.callAsync(takerAddress); - const takerAssetFillAmount = takerAssetAmount.dividedToIntegerBy(2); - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { + await fillOrderWrapper.fillOrderAndAssertEffectsAsync(signedOrder, takerAddress, { takerAssetFillAmount, }); - - const finalMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress); - const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const finalMakerFeeTokenBalance = await feeToken.balanceOf.callAsync(makerAddress); - const finalTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress); - const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - const finalTakerFeeTokenBalance = await feeToken.balanceOf.callAsync(takerAddress); - - expect(finalMakerBalanceA).to.be.bignumber.equal( - initialMakerBalanceA.plus( - takerAmounts[0].times( - takerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), - ), - ), - ); - expect(finalMakerBalanceB).to.be.bignumber.equal( - initialMakerBalanceB.plus( - takerAmounts[1].times( - takerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), - ), - ), - ); - expect(finalMakerFeeTokenBalance).to.be.bignumber.equal( - initialMakerFeeTokenBalance.minus( - makerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), - ), - ); - expect(finalTakerBalanceA).to.be.bignumber.equal( - initialTakerBalanceA.minus( - takerAmounts[0].times( - takerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), - ), - ), - ); - expect(finalTakerBalanceB).to.be.bignumber.equal( - initialTakerBalanceB.minus( - takerAmounts[1].times( - takerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), - ), - ), - ); - expect(finalTakerFeeTokenBalance).to.be.bignumber.equal( - initialTakerFeeTokenBalance.plus( - makerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), - ), - ); }); }); describe('Testing exchange of erc1155 assets', () => { it('should allow a single fungible erc1155 asset to be exchanged for another', async () => { // setup test parameters - const tokenHolders = [makerAddress, takerAddress]; const makerAssetsToTransfer = erc1155FungibleTokens.slice(0, 1); const takerAssetsToTransfer = erc1155FungibleTokens.slice(1, 2); const makerValuesToTransfer = [new BigNumber(500)]; const takerValuesToTransfer = [new BigNumber(200)]; - const tokensToTransfer = makerAssetsToTransfer.concat(takerAssetsToTransfer); const makerAssetAmount = new BigNumber(1); const takerAssetAmount = new BigNumber(1); - const totalMakerValuesTransferred = _.map(makerValuesToTransfer, (value: BigNumber) => { - return value.times(makerAssetAmount); - }); - const totalTakerValuesTransferred = _.map(takerValuesToTransfer, (value: BigNumber) => { - return value.times(takerAssetAmount); - }); const receiverCallbackData = '0x'; const makerAssetData = assetDataUtils.encodeERC1155AssetData( erc1155Contract.address, @@ -1362,51 +943,19 @@ describe('Exchange core', () => { takerFee: constants.ZERO_AMOUNT, }); const takerAssetFillAmount = new BigNumber(1); - // check balances before transfer - const expectedInitialBalances = [ - // makerAddress / makerToken - constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, - // makerAddress / takerToken - constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, - // takerAddress / makerToken - constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, - // takerAddress / takerToken - constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, - ]; - await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); // execute transfer - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { + await fillOrderWrapper.fillOrderAndAssertEffectsAsync(signedOrder, takerAddress, { takerAssetFillAmount, }); - // check balances after transfer - const expectedFinalBalances = [ - // makerAddress / makerToken - expectedInitialBalances[0].minus(totalMakerValuesTransferred[0]), - // makerAddress / takerToken - expectedInitialBalances[1].plus(totalTakerValuesTransferred[0]), - // takerAddress / makerToken - expectedInitialBalances[2].plus(totalMakerValuesTransferred[0]), - // takerAddress / takerToken - expectedInitialBalances[3].minus(totalTakerValuesTransferred[0]), - ]; - await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); }); it('should allow a single non-fungible erc1155 asset to be exchanged for another', async () => { // setup test parameters - const tokenHolders = [makerAddress, takerAddress]; const makerAssetsToTransfer = erc1155NonFungibleTokensOwnedByMaker.slice(0, 1); const takerAssetsToTransfer = erc1155NonFungibleTokensOwnedByTaker.slice(0, 1); const makerValuesToTransfer = [new BigNumber(1)]; const takerValuesToTransfer = [new BigNumber(1)]; - const tokensToTransfer = makerAssetsToTransfer.concat(takerAssetsToTransfer); const makerAssetAmount = new BigNumber(1); const takerAssetAmount = new BigNumber(1); - const totalMakerValuesTransferred = _.map(makerValuesToTransfer, (value: BigNumber) => { - return value.times(makerAssetAmount); - }); - const totalTakerValuesTransferred = _.map(takerValuesToTransfer, (value: BigNumber) => { - return value.times(takerAssetAmount); - }); const receiverCallbackData = '0x'; const makerAssetData = assetDataUtils.encodeERC1155AssetData( erc1155Contract.address, @@ -1429,53 +978,18 @@ describe('Exchange core', () => { takerFee: constants.ZERO_AMOUNT, }); const takerAssetFillAmount = new BigNumber(1); - // check balances before transfer - const nftOwnerBalance = new BigNumber(1); - const nftNotOwnerBalance = new BigNumber(0); - const expectedInitialBalances = [ - // makerAddress / makerToken - nftOwnerBalance, - // makerAddress / takerToken - nftNotOwnerBalance, - // takerAddress / makerToken - nftNotOwnerBalance, - // takerAddress / takerToken - nftOwnerBalance, - ]; - await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); - // execute transfer - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { + await fillOrderWrapper.fillOrderAndAssertEffectsAsync(signedOrder, takerAddress, { takerAssetFillAmount, }); - // check balances after transfer - const expectedFinalBalances = [ - // makerAddress / makerToken - expectedInitialBalances[0].minus(totalMakerValuesTransferred[0]), - // makerAddress / takerToken - expectedInitialBalances[1].plus(totalTakerValuesTransferred[0]), - // takerAddress / makerToken - expectedInitialBalances[2].plus(totalMakerValuesTransferred[0]), - // takerAddress / takerToken - expectedInitialBalances[3].minus(totalTakerValuesTransferred[0]), - ]; - await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); }); it('should allow multiple erc1155 assets to be exchanged for a single asset', async () => { // setup test parameters - const tokenHolders = [makerAddress, takerAddress]; const makerAssetsToTransfer = erc1155FungibleTokens.slice(0, 3); const takerAssetsToTransfer = erc1155NonFungibleTokensOwnedByTaker.slice(0, 1); const makerValuesToTransfer = [new BigNumber(500), new BigNumber(700), new BigNumber(900)]; const takerValuesToTransfer = [new BigNumber(1)]; - const tokensToTransfer = makerAssetsToTransfer.concat(takerAssetsToTransfer); const makerAssetAmount = new BigNumber(1); const takerAssetAmount = new BigNumber(1); - const totalMakerValuesTransferred = _.map(makerValuesToTransfer, (value: BigNumber) => { - return value.times(makerAssetAmount); - }); - const totalTakerValuesTransferred = _.map(takerValuesToTransfer, (value: BigNumber) => { - return value.times(takerAssetAmount); - }); const receiverCallbackData = '0x'; const makerAssetData = assetDataUtils.encodeERC1155AssetData( erc1155Contract.address, @@ -1498,58 +1012,14 @@ describe('Exchange core', () => { takerFee: constants.ZERO_AMOUNT, }); const takerAssetFillAmount = new BigNumber(1); - // check balances before transfer - const nftOwnerBalance = new BigNumber(1); - const nftNotOwnerBalance = new BigNumber(0); - const expectedInitialBalances = [ - // makerAddress / makerToken[0] - constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, - // makerAddress / makerToken[1] - constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, - // makerAddress / makerToken[2] - constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, - // makerAddress / takerToken - nftNotOwnerBalance, - // takerAddress / makerToken[0] - constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, - // takerAddress / makerToken[1] - constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, - // takerAddress / makerToken[2] - constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, - // takerAddress / takerToken - nftOwnerBalance, - ]; - await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); - // execute transfer - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { + await fillOrderWrapper.fillOrderAndAssertEffectsAsync(signedOrder, takerAddress, { takerAssetFillAmount, }); - // check balances after transfer - const expectedFinalBalances = [ - // makerAddress / makerToken[0] - expectedInitialBalances[0].minus(totalMakerValuesTransferred[0]), - // makerAddress / makerToken[1] - expectedInitialBalances[1].minus(totalMakerValuesTransferred[1]), - // makerAddress / makerToken[2] - expectedInitialBalances[2].minus(totalMakerValuesTransferred[2]), - // makerAddress / takerToken - expectedInitialBalances[3].plus(totalTakerValuesTransferred[0]), - // takerAddress / makerToken[0] - expectedInitialBalances[4].plus(totalMakerValuesTransferred[0]), - // takerAddress / makerToken[1] - expectedInitialBalances[5].plus(totalMakerValuesTransferred[1]), - // takerAddress / makerToken[2] - expectedInitialBalances[6].plus(totalMakerValuesTransferred[2]), - // takerAddress / takerToken - expectedInitialBalances[7].minus(totalTakerValuesTransferred[0]), - ]; - await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); }); it('should allow multiple erc1155 assets to be exchanged for multiple erc1155 assets, mixed fungible/non-fungible', async () => { // setup test parameters // the maker is trading two fungibles & one non-fungible // the taker is trading one fungible & two non-fungibles - const tokenHolders = [makerAddress, takerAddress]; const makerFungibleAssetsToTransfer = erc1155FungibleTokens.slice(0, 2); const makerNonFungibleAssetsToTransfer = erc1155NonFungibleTokensOwnedByMaker.slice(0, 1); const makerAssetsToTransfer = makerFungibleAssetsToTransfer.concat(makerNonFungibleAssetsToTransfer); @@ -1558,15 +1028,8 @@ describe('Exchange core', () => { const takerAssetsToTransfer = takerFungibleAssetsToTransfer.concat(takerNonFungibleAssetsToTransfer); const makerValuesToTransfer = [new BigNumber(500), new BigNumber(700), new BigNumber(1)]; const takerValuesToTransfer = [new BigNumber(900), new BigNumber(1), new BigNumber(1)]; - const tokensToTransfer = makerAssetsToTransfer.concat(takerAssetsToTransfer); const makerAssetAmount = new BigNumber(1); const takerAssetAmount = new BigNumber(1); - const totalMakerValuesTransferred = _.map(makerValuesToTransfer, (value: BigNumber) => { - return value.times(makerAssetAmount); - }); - const totalTakerValuesTransferred = _.map(takerValuesToTransfer, (value: BigNumber) => { - return value.times(takerAssetAmount); - }); const receiverCallbackData = '0x'; const makerAssetData = assetDataUtils.encodeERC1155AssetData( erc1155Contract.address, @@ -1589,68 +1052,9 @@ describe('Exchange core', () => { takerFee: constants.ZERO_AMOUNT, }); const takerAssetFillAmount = new BigNumber(1); - // check balances before transfer - const nftOwnerBalance = new BigNumber(1); - const nftNotOwnerBalance = new BigNumber(0); - const expectedInitialBalances = [ - // makerAddress / makerToken[0] - constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, - // makerAddress / makerToken[1] - constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, - // makerAddress / makerToken[2] - nftOwnerBalance, - // makerAddress / takerToken[0] - constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, - // makerAddress / takerToken[1] - nftNotOwnerBalance, - // makerAddress / takerToken[2] - nftNotOwnerBalance, - // takerAddress / makerToken[0] - constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, - // takerAddress / makerToken[1] - constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, - // takerAddress / makerToken[2] - nftNotOwnerBalance, - // takerAddress / takerToken[0] - constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, - // takerAddress / takerToken[1] - nftOwnerBalance, - // takerAddress / takerToken[2] - nftOwnerBalance, - ]; - await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); - // execute transfer - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { + await fillOrderWrapper.fillOrderAndAssertEffectsAsync(signedOrder, takerAddress, { takerAssetFillAmount, }); - // check balances after transfer - const expectedFinalBalances = [ - // makerAddress / makerToken[0] - expectedInitialBalances[0].minus(totalMakerValuesTransferred[0]), - // makerAddress / makerToken[1] - expectedInitialBalances[1].minus(totalMakerValuesTransferred[1]), - // makerAddress / makerToken[2] - expectedInitialBalances[2].minus(totalMakerValuesTransferred[2]), - // makerAddress / takerToken[0] - expectedInitialBalances[3].plus(totalTakerValuesTransferred[0]), - // makerAddress / takerToken[1] - expectedInitialBalances[4].plus(totalTakerValuesTransferred[1]), - // makerAddress / takerToken[2] - expectedInitialBalances[5].plus(totalTakerValuesTransferred[2]), - // takerAddress / makerToken[0] - expectedInitialBalances[6].plus(totalMakerValuesTransferred[0]), - // takerAddress / makerToken[1] - expectedInitialBalances[7].plus(totalMakerValuesTransferred[1]), - // takerAddress / makerToken[2] - expectedInitialBalances[8].plus(totalMakerValuesTransferred[2]), - // takerAddress / takerToken[0] - expectedInitialBalances[9].minus(totalTakerValuesTransferred[0]), - // takerAddress / takerToken[1] - expectedInitialBalances[10].minus(totalTakerValuesTransferred[1]), - // takerAddress / takerToken[2] - expectedInitialBalances[11].minus(totalTakerValuesTransferred[2]), - ]; - await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); }); it('should allow an order exchanging erc1155 assets to be partially filled', async () => { // NOTICE: @@ -1665,26 +1069,12 @@ describe('Exchange core', () => { // note that this will result in a partial fill because the `takerAssetAmount` // less than the `takerAssetAmount` of the order. const takerAssetFillAmount = new BigNumber(6); - const tokenHolders = [makerAddress, takerAddress]; const makerAssetsToTransfer = erc1155FungibleTokens.slice(0, 2); const takerAssetsToTransfer = erc1155FungibleTokens.slice(2, 3); const makerValuesToTransfer = [new BigNumber(500), new BigNumber(700)]; const takerValuesToTransfer = [new BigNumber(900)]; - const tokensToTransfer = makerAssetsToTransfer.concat(takerAssetsToTransfer); const makerAssetAmount = new BigNumber(10); const takerAssetAmount = new BigNumber(20); - const totalMakerValuesTransferred = _.map(makerValuesToTransfer, (value: BigNumber) => { - return value - .times(makerAssetAmount) - .times(takerAssetFillAmount) - .dividedToIntegerBy(takerAssetAmount); - }); - const totalTakerValuesTransferred = _.map(takerValuesToTransfer, (value: BigNumber) => { - return value - .times(takerAssetAmount) - .times(takerAssetFillAmount) - .dividedToIntegerBy(takerAssetAmount); - }); const receiverCallbackData = '0x'; const makerAssetData = assetDataUtils.encodeERC1155AssetData( erc1155Contract.address, @@ -1706,50 +1096,9 @@ describe('Exchange core', () => { makerFee: constants.ZERO_AMOUNT, takerFee: constants.ZERO_AMOUNT, }); - // check balances before transfer - const expectedInitialBalances = [ - // makerAddress / makerToken[0] - constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, - // makerAddress / makerToken[1] - constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, - // makerAddress / takerToken[0] - constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, - // takerAddress / makerToken[0] - constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, - // takerAddress / makerToken[1] - constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, - // takerAddress / takerToken[0] - constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, - ]; - await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); - // execute transfer - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { + await fillOrderWrapper.fillOrderAndAssertEffectsAsync(signedOrder, takerAddress, { takerAssetFillAmount, }); - // check balances after transfer - const expectedFinalBalances = [ - // makerAddress / makerToken[0] - expectedInitialBalances[0].minus(totalMakerValuesTransferred[0]), - // makerAddress / makerToken[1] - expectedInitialBalances[1].minus(totalMakerValuesTransferred[1]), - // makerAddress / takerToken[0] - expectedInitialBalances[2].plus(totalTakerValuesTransferred[0]), - // takerAddress / makerToken[0] - expectedInitialBalances[3].plus(totalMakerValuesTransferred[0]), - // takerAddress / makerToken[1] - expectedInitialBalances[4].plus(totalMakerValuesTransferred[1]), - // takerAddress / takerToken[0] - expectedInitialBalances[5].minus(totalTakerValuesTransferred[0]), - ]; - await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); - // check that the order is partially filled - const orderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrder); - const expectedOrderHash = orderHashUtils.getOrderHashHex(signedOrder); - const expectedTakerAssetFilledAmount = takerAssetFillAmount; - const expectedOrderStatus = OrderStatus.Fillable; - expect(orderInfo.orderHash).to.be.equal(expectedOrderHash); - expect(orderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal(expectedTakerAssetFilledAmount); - expect(orderInfo.orderStatus).to.equal(expectedOrderStatus); }); }); @@ -1788,32 +1137,7 @@ describe('Exchange core', () => { constants.KECCAK256_NULL, ); signedOrder = await orderFactory.newSignedOrderAsync({ makerAssetData: assetData }); - - const initialMakerFeeTokenBalance = await feeToken.balanceOf.callAsync(makerAddress); - const initialTakerFeeTokenBalance = await feeToken.balanceOf.callAsync(takerAddress); - const initialFeeRecipientFeeTokenBalance = await feeToken.balanceOf.callAsync(feeRecipientAddress); - const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); - - const finalMakerFeeTokenBalance = await feeToken.balanceOf.callAsync(makerAddress); - const finalTakerFeeTokenBalance = await feeToken.balanceOf.callAsync(takerAddress); - const finalFeeRecipientFeeTokenBalance = await feeToken.balanceOf.callAsync(feeRecipientAddress); - const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - - expect(finalMakerFeeTokenBalance).to.bignumber.equal( - initialMakerFeeTokenBalance.minus(signedOrder.makerFee), - ); - expect(finalTakerFeeTokenBalance).to.bignumber.equal( - initialTakerFeeTokenBalance.minus(signedOrder.takerFee), - ); - expect(finalFeeRecipientFeeTokenBalance).to.bignumber.equal( - initialFeeRecipientFeeTokenBalance.plus(signedOrder.makerFee).plus(signedOrder.takerFee), - ); - expect(finalMakerBalanceB).to.bignumber.equal(initialMakerBalanceB.plus(signedOrder.takerAssetAmount)); - expect(finalTakerBalanceB).to.bignumber.equal(initialTakerBalanceB.minus(signedOrder.takerAssetAmount)); + await fillOrderWrapper.fillOrderAndAssertEffectsAsync(signedOrder, takerAddress); }); it('should revert if the staticcall is unsuccessful using the MultiAssetProxy', async () => { const staticCallData = staticCallTarget.assertEvenNumber.getABIEncodedTransactionData(new BigNumber(1)); @@ -1850,38 +1174,7 @@ describe('Exchange core', () => { [assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress), staticCallAssetData], ); signedOrder = await orderFactory.newSignedOrderAsync({ makerAssetData: assetData }); - - const initialMakerFeeTokenBalance = await feeToken.balanceOf.callAsync(makerAddress); - const initialTakerFeeTokenBalance = await feeToken.balanceOf.callAsync(takerAddress); - const initialFeeRecipientFeeTokenBalance = await feeToken.balanceOf.callAsync(feeRecipientAddress); - const initialMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress); - const initialTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress); - const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); - - const finalMakerFeeTokenBalance = await feeToken.balanceOf.callAsync(makerAddress); - const finalTakerFeeTokenBalance = await feeToken.balanceOf.callAsync(takerAddress); - const finalFeeRecipientFeeTokenBalance = await feeToken.balanceOf.callAsync(feeRecipientAddress); - const finalMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress); - const finalTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress); - const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); - const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); - - expect(finalMakerFeeTokenBalance).to.bignumber.equal( - initialMakerFeeTokenBalance.minus(signedOrder.makerFee), - ); - expect(finalTakerFeeTokenBalance).to.bignumber.equal( - initialTakerFeeTokenBalance.minus(signedOrder.takerFee), - ); - expect(finalFeeRecipientFeeTokenBalance).to.bignumber.equal( - initialFeeRecipientFeeTokenBalance.plus(signedOrder.makerFee).plus(signedOrder.takerFee), - ); - expect(finalMakerBalanceA).to.bignumber.equal(initialMakerBalanceA.minus(signedOrder.makerAssetAmount)); - expect(finalTakerBalanceA).to.bignumber.equal(initialTakerBalanceA.plus(signedOrder.makerAssetAmount)); - expect(finalMakerBalanceB).to.bignumber.equal(initialMakerBalanceB.plus(signedOrder.takerAssetAmount)); - expect(finalTakerBalanceB).to.bignumber.equal(initialTakerBalanceB.minus(signedOrder.takerAssetAmount)); + await fillOrderWrapper.fillOrderAndAssertEffectsAsync(signedOrder, takerAddress); }); }); @@ -1899,25 +1192,11 @@ describe('Exchange core', () => { expect(orderInfo.orderStatus).to.equal(expectedOrderStatus); }); it('should return the correct orderInfo for a fully filled order', async () => { - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); - const orderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrder); - const expectedOrderHash = orderHashUtils.getOrderHashHex(signedOrder); - const expectedTakerAssetFilledAmount = signedOrder.takerAssetAmount; - const expectedOrderStatus = OrderStatus.FullyFilled; - expect(orderInfo.orderHash).to.be.equal(expectedOrderHash); - expect(orderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal(expectedTakerAssetFilledAmount); - expect(orderInfo.orderStatus).to.equal(expectedOrderStatus); + await fillOrderWrapper.fillOrderAndAssertEffectsAsync(signedOrder, takerAddress); }); it('should return the correct orderInfo for a partially filled order', async () => { const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2); - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }); - const orderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrder); - const expectedOrderHash = orderHashUtils.getOrderHashHex(signedOrder); - const expectedTakerAssetFilledAmount = takerAssetFillAmount; - const expectedOrderStatus = OrderStatus.Fillable; - expect(orderInfo.orderHash).to.be.equal(expectedOrderHash); - expect(orderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal(expectedTakerAssetFilledAmount); - expect(orderInfo.orderStatus).to.equal(expectedOrderStatus); + await fillOrderWrapper.fillOrderAndAssertEffectsAsync(signedOrder, takerAddress, { takerAssetFillAmount }); }); it('should return the correct orderInfo for a cancelled and unfilled order', async () => { await exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress); diff --git a/contracts/test-utils/src/index.ts b/contracts/test-utils/src/index.ts index ea1ff0839c..f6eac0048d 100644 --- a/contracts/test-utils/src/index.ts +++ b/contracts/test-utils/src/index.ts @@ -35,12 +35,15 @@ export { ERC20BalancesByOwner, ERC1155FungibleHoldingsByOwner, ERC1155HoldingsByOwner, + ERC1155Holdings, ERC1155NonFungibleHoldingsByOwner, ERC721TokenIdsByOwner, + FillEventArgs, MarketBuyOrders, MarketSellOrders, OrderStatus, Token, + TokenBalances, TransactionDataParams, } from './types'; export { blockchainTests, BlockchainTestsEnvironment, describe } from './mocha_blockchain'; diff --git a/contracts/test-utils/src/types.ts b/contracts/test-utils/src/types.ts index 84fdd18b52..164777abeb 100644 --- a/contracts/test-utils/src/types.ts +++ b/contracts/test-utils/src/types.ts @@ -137,3 +137,38 @@ export interface MatchOrder { leftSignature: string; rightSignature: string; } + +export interface ERC1155Holdings { + [owner: string]: { + [contract: string]: { + fungible: { + [tokenId: string]: BigNumber; + }; + nonFungible: BigNumber[]; + }; + }; +} + +export interface TokenBalances { + erc20: { + [owner: string]: { + [contract: string]: BigNumber; + }; + }; + erc721: { + [owner: string]: { + [contract: string]: BigNumber[]; + }; + }; + erc1155: ERC1155Holdings; +} + +export interface FillEventArgs { + orderHash: string; + makerAddress: string; + takerAddress: string; + makerAssetFilledAmount: BigNumber; + takerAssetFilledAmount: BigNumber; + makerFeePaid: BigNumber; + takerFeePaid: BigNumber; +}