aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contracts/test/utils/match_order_tester.ts
diff options
context:
space:
mode:
authorFabio Berger <me@fabioberger.com>2018-07-02 17:12:01 +0800
committerFabio Berger <me@fabioberger.com>2018-07-02 17:12:01 +0800
commitde9f0732a09893f035ce8a7e8e01fa1141882a3a (patch)
tree63d1f0bbc6f9d65576e5d12b379378700eb88567 /packages/contracts/test/utils/match_order_tester.ts
parent20acdbf6c3ba6a62e87a9a496021cb6482c0c03a (diff)
parentb9b00e10d39c3c84bc72892ef37f1313e904414d (diff)
downloaddexon-sol-tools-de9f0732a09893f035ce8a7e8e01fa1141882a3a.tar
dexon-sol-tools-de9f0732a09893f035ce8a7e8e01fa1141882a3a.tar.gz
dexon-sol-tools-de9f0732a09893f035ce8a7e8e01fa1141882a3a.tar.bz2
dexon-sol-tools-de9f0732a09893f035ce8a7e8e01fa1141882a3a.tar.lz
dexon-sol-tools-de9f0732a09893f035ce8a7e8e01fa1141882a3a.tar.xz
dexon-sol-tools-de9f0732a09893f035ce8a7e8e01fa1141882a3a.tar.zst
dexon-sol-tools-de9f0732a09893f035ce8a7e8e01fa1141882a3a.zip
Merge branch 'v2-prototype' into fix/five_decimal_scenario
* v2-prototype: (75 commits) Update relayer grid tiles to use Text Fix build Update file structure Update 2.0.0 artifacts Move ledgerhq module declarations to typescript-typings Export LedgerEthereumClient type in subproviders Update artifacts Add logging and updated artifacts Fix migrations Run prettier Add Kovan artifacts Use ledger subprovider Add Kovan migrations Remove state variable from Link component in Portal Make registerAssetProxy append only Update staging api link Change getTransactionReceipt to awaitTransactionMined Move /docs route to the end Remove extra call to scrollIntoView for wallet in onboarding Update expectRevertReasonOrAlwaysFailingTransactionAsync to check status codes ...
Diffstat (limited to 'packages/contracts/test/utils/match_order_tester.ts')
-rw-r--r--packages/contracts/test/utils/match_order_tester.ts326
1 files changed, 326 insertions, 0 deletions
diff --git a/packages/contracts/test/utils/match_order_tester.ts b/packages/contracts/test/utils/match_order_tester.ts
new file mode 100644
index 000000000..6145779b0
--- /dev/null
+++ b/packages/contracts/test/utils/match_order_tester.ts
@@ -0,0 +1,326 @@
+import { assetProxyUtils, orderHashUtils } from '@0xproject/order-utils';
+import { AssetProxyId, SignedOrder } from '@0xproject/types';
+import { BigNumber } from '@0xproject/utils';
+import * as chai from 'chai';
+import * as _ from 'lodash';
+
+import { chaiSetup } from './chai_setup';
+import { ERC20Wrapper } from './erc20_wrapper';
+import { ERC721Wrapper } from './erc721_wrapper';
+import { ExchangeWrapper } from './exchange_wrapper';
+import { ERC20BalancesByOwner, ERC721TokenIdsByOwner, TransferAmountsByMatchOrders as TransferAmounts } from './types';
+
+chaiSetup.configure();
+const expect = chai.expect;
+
+export class MatchOrderTester {
+ private _exchangeWrapper: ExchangeWrapper;
+ private _erc20Wrapper: ERC20Wrapper;
+ private _erc721Wrapper: ERC721Wrapper;
+ private _feeTokenAddress: string;
+
+ /// @dev Compares a pair of ERC20 balances and a pair of ERC721 token owners.
+ /// @param expectedNewERC20BalancesByOwner Expected ERC20 balances.
+ /// @param realERC20BalancesByOwner Actual ERC20 balances.
+ /// @param expectedNewERC721TokenIdsByOwner Expected ERC721 token owners.
+ /// @param realERC721TokenIdsByOwner Actual ERC20 token owners.
+ /// @return True only if ERC20 balances match and ERC721 token owners match.
+ private static _compareExpectedAndRealBalances(
+ expectedNewERC20BalancesByOwner: ERC20BalancesByOwner,
+ realERC20BalancesByOwner: ERC20BalancesByOwner,
+ expectedNewERC721TokenIdsByOwner: ERC721TokenIdsByOwner,
+ realERC721TokenIdsByOwner: ERC721TokenIdsByOwner,
+ ): boolean {
+ // ERC20 Balances
+ const doesErc20BalancesMatch = _.isEqual(expectedNewERC20BalancesByOwner, realERC20BalancesByOwner);
+ if (!doesErc20BalancesMatch) {
+ return false;
+ }
+ // ERC721 Token Ids
+ const sortedExpectedNewERC721TokenIdsByOwner = _.mapValues(
+ expectedNewERC721TokenIdsByOwner,
+ tokenIdsByOwner => {
+ _.mapValues(tokenIdsByOwner, tokenIds => {
+ _.sortBy(tokenIds);
+ });
+ },
+ );
+ const sortedNewERC721TokenIdsByOwner = _.mapValues(realERC721TokenIdsByOwner, tokenIdsByOwner => {
+ _.mapValues(tokenIdsByOwner, tokenIds => {
+ _.sortBy(tokenIds);
+ });
+ });
+ const doesErc721TokenIdsMatch = _.isEqual(
+ sortedExpectedNewERC721TokenIdsByOwner,
+ sortedNewERC721TokenIdsByOwner,
+ );
+ return doesErc721TokenIdsMatch;
+ }
+ /// @dev Constructs new MatchOrderTester.
+ /// @param exchangeWrapper Used to call to the Exchange.
+ /// @param erc20Wrapper Used to fetch ERC20 balances.
+ /// @param erc721Wrapper Used to fetch ERC721 token owners.
+ /// @param feeTokenAddress Address of ERC20 fee token.
+ constructor(
+ exchangeWrapper: ExchangeWrapper,
+ erc20Wrapper: ERC20Wrapper,
+ erc721Wrapper: ERC721Wrapper,
+ feeTokenAddress: string,
+ ) {
+ this._exchangeWrapper = exchangeWrapper;
+ this._erc20Wrapper = erc20Wrapper;
+ this._erc721Wrapper = erc721Wrapper;
+ this._feeTokenAddress = feeTokenAddress;
+ }
+ /// @dev Matches two complementary orders and validates results.
+ /// Validation either succeeds or throws.
+ /// @param signedOrderLeft First matched order.
+ /// @param signedOrderRight Second matched order.
+ /// @param takerAddress Address of taker (the address who matched the two orders)
+ /// @param erc20BalancesByOwner Current ERC20 balances.
+ /// @param erc721TokenIdsByOwner Current ERC721 token owners.
+ /// @param initialTakerAssetFilledAmountLeft Current amount the left order has been filled.
+ /// @param initialTakerAssetFilledAmountRight Current amount the right order has been filled.
+ /// @return New ERC20 balances & ERC721 token owners.
+ public async matchOrdersAndVerifyBalancesAsync(
+ signedOrderLeft: SignedOrder,
+ signedOrderRight: SignedOrder,
+ takerAddress: string,
+ erc20BalancesByOwner: ERC20BalancesByOwner,
+ erc721TokenIdsByOwner: ERC721TokenIdsByOwner,
+ initialTakerAssetFilledAmountLeft?: BigNumber,
+ initialTakerAssetFilledAmountRight?: BigNumber,
+ ): Promise<[ERC20BalancesByOwner, ERC721TokenIdsByOwner]> {
+ // Verify Left order preconditions
+ const orderTakerAssetFilledAmountLeft = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
+ orderHashUtils.getOrderHashHex(signedOrderLeft),
+ );
+ const expectedOrderFilledAmountLeft = initialTakerAssetFilledAmountLeft
+ ? initialTakerAssetFilledAmountLeft
+ : new BigNumber(0);
+ expect(expectedOrderFilledAmountLeft).to.be.bignumber.equal(orderTakerAssetFilledAmountLeft);
+ // Verify Right order preconditions
+ const orderTakerAssetFilledAmountRight = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
+ orderHashUtils.getOrderHashHex(signedOrderRight),
+ );
+ const expectedOrderFilledAmountRight = initialTakerAssetFilledAmountRight
+ ? initialTakerAssetFilledAmountRight
+ : new BigNumber(0);
+ expect(expectedOrderFilledAmountRight).to.be.bignumber.equal(orderTakerAssetFilledAmountRight);
+ // Match left & right orders
+ await this._exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress);
+ const newERC20BalancesByOwner = await this._erc20Wrapper.getBalancesAsync();
+ const newERC721TokenIdsByOwner = await this._erc721Wrapper.getBalancesAsync();
+ // Calculate expected balance changes
+ const expectedTransferAmounts = await this._calculateExpectedTransferAmountsAsync(
+ signedOrderLeft,
+ signedOrderRight,
+ orderTakerAssetFilledAmountLeft,
+ orderTakerAssetFilledAmountRight,
+ );
+ let expectedERC20BalancesByOwner: ERC20BalancesByOwner;
+ let expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner;
+ [expectedERC20BalancesByOwner, expectedERC721TokenIdsByOwner] = this._calculateExpectedBalances(
+ signedOrderLeft,
+ signedOrderRight,
+ takerAddress,
+ erc20BalancesByOwner,
+ erc721TokenIdsByOwner,
+ expectedTransferAmounts,
+ );
+ // Assert our expected balances are equal to the actual balances
+ const didExpectedBalancesMatchRealBalances = MatchOrderTester._compareExpectedAndRealBalances(
+ expectedERC20BalancesByOwner,
+ newERC20BalancesByOwner,
+ expectedERC721TokenIdsByOwner,
+ newERC721TokenIdsByOwner,
+ );
+ expect(didExpectedBalancesMatchRealBalances).to.be.true();
+ return [newERC20BalancesByOwner, newERC721TokenIdsByOwner];
+ }
+ /// @dev Calculates expected transfer amounts between order makers, fee recipients, and
+ /// the taker when two orders are matched.
+ /// @param signedOrderLeft First matched order.
+ /// @param signedOrderRight Second matched order.
+ /// @param orderTakerAssetFilledAmountLeft How much left order has been filled, prior to matching orders.
+ /// @param orderTakerAssetFilledAmountRight How much the right order has been filled, prior to matching orders.
+ /// @return TransferAmounts A struct containing the expected transfer amounts.
+ private async _calculateExpectedTransferAmountsAsync(
+ signedOrderLeft: SignedOrder,
+ signedOrderRight: SignedOrder,
+ orderTakerAssetFilledAmountLeft: BigNumber,
+ orderTakerAssetFilledAmountRight: BigNumber,
+ ): Promise<TransferAmounts> {
+ let amountBoughtByLeftMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
+ orderHashUtils.getOrderHashHex(signedOrderLeft),
+ );
+ amountBoughtByLeftMaker = amountBoughtByLeftMaker.minus(orderTakerAssetFilledAmountLeft);
+ const amountSoldByLeftMaker = amountBoughtByLeftMaker
+ .times(signedOrderLeft.makerAssetAmount)
+ .dividedToIntegerBy(signedOrderLeft.takerAssetAmount);
+ const amountReceivedByRightMaker = amountBoughtByLeftMaker
+ .times(signedOrderRight.takerAssetAmount)
+ .dividedToIntegerBy(signedOrderRight.makerAssetAmount);
+ const amountReceivedByTaker = amountSoldByLeftMaker.minus(amountReceivedByRightMaker);
+ let amountBoughtByRightMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
+ orderHashUtils.getOrderHashHex(signedOrderRight),
+ );
+ amountBoughtByRightMaker = amountBoughtByRightMaker.minus(orderTakerAssetFilledAmountRight);
+ const amountSoldByRightMaker = amountBoughtByRightMaker
+ .times(signedOrderRight.makerAssetAmount)
+ .dividedToIntegerBy(signedOrderRight.takerAssetAmount);
+ const amountReceivedByLeftMaker = amountSoldByRightMaker;
+ const feePaidByLeftMaker = signedOrderLeft.makerFee
+ .times(amountSoldByLeftMaker)
+ .dividedToIntegerBy(signedOrderLeft.makerAssetAmount);
+ const feePaidByRightMaker = signedOrderRight.makerFee
+ .times(amountSoldByRightMaker)
+ .dividedToIntegerBy(signedOrderRight.makerAssetAmount);
+ const feePaidByTakerLeft = signedOrderLeft.takerFee
+ .times(amountSoldByLeftMaker)
+ .dividedToIntegerBy(signedOrderLeft.makerAssetAmount);
+ const feePaidByTakerRight = signedOrderRight.takerFee
+ .times(amountSoldByRightMaker)
+ .dividedToIntegerBy(signedOrderRight.makerAssetAmount);
+ const totalFeePaidByTaker = feePaidByTakerLeft.add(feePaidByTakerRight);
+ const feeReceivedLeft = feePaidByLeftMaker.add(feePaidByTakerLeft);
+ const feeReceivedRight = feePaidByRightMaker.add(feePaidByTakerRight);
+ // Return values
+ const expectedTransferAmounts = {
+ // Left Maker
+ amountBoughtByLeftMaker,
+ amountSoldByLeftMaker,
+ amountReceivedByLeftMaker,
+ feePaidByLeftMaker,
+ // Right Maker
+ amountBoughtByRightMaker,
+ amountSoldByRightMaker,
+ amountReceivedByRightMaker,
+ feePaidByRightMaker,
+ // Taker
+ amountReceivedByTaker,
+ feePaidByTakerLeft,
+ feePaidByTakerRight,
+ totalFeePaidByTaker,
+ // Fee Recipients
+ feeReceivedLeft,
+ feeReceivedRight,
+ };
+ return expectedTransferAmounts;
+ }
+ /// @dev Calculates the expected balances of order makers, fee recipients, and the taker,
+ /// as a result of matching two orders.
+ /// @param signedOrderLeft First matched order.
+ /// @param signedOrderRight Second matched order.
+ /// @param takerAddress Address of taker (the address who matched the two orders)
+ /// @param erc20BalancesByOwner Current ERC20 balances.
+ /// @param erc721TokenIdsByOwner Current ERC721 token owners.
+ /// @param expectedTransferAmounts A struct containing the expected transfer amounts.
+ /// @return Expected ERC20 balances & ERC721 token owners after orders have been matched.
+ private _calculateExpectedBalances(
+ signedOrderLeft: SignedOrder,
+ signedOrderRight: SignedOrder,
+ takerAddress: string,
+ erc20BalancesByOwner: ERC20BalancesByOwner,
+ erc721TokenIdsByOwner: ERC721TokenIdsByOwner,
+ expectedTransferAmounts: TransferAmounts,
+ ): [ERC20BalancesByOwner, ERC721TokenIdsByOwner] {
+ const makerAddressLeft = signedOrderLeft.makerAddress;
+ const makerAddressRight = signedOrderRight.makerAddress;
+ const feeRecipientAddressLeft = signedOrderLeft.feeRecipientAddress;
+ const feeRecipientAddressRight = signedOrderRight.feeRecipientAddress;
+ // Operations are performed on copies of the balances
+ const expectedNewERC20BalancesByOwner = _.cloneDeep(erc20BalancesByOwner);
+ const expectedNewERC721TokenIdsByOwner = _.cloneDeep(erc721TokenIdsByOwner);
+ // Left Maker Asset (Right Taker Asset)
+ const makerAssetProxyIdLeft = assetProxyUtils.decodeAssetDataId(signedOrderLeft.makerAssetData);
+ if (makerAssetProxyIdLeft === AssetProxyId.ERC20) {
+ // Decode asset data
+ const erc20AssetData = assetProxyUtils.decodeERC20AssetData(signedOrderLeft.makerAssetData);
+ const makerAssetAddressLeft = erc20AssetData.tokenAddress;
+ const takerAssetAddressRight = makerAssetAddressLeft;
+ // Left Maker
+ expectedNewERC20BalancesByOwner[makerAddressLeft][makerAssetAddressLeft] = expectedNewERC20BalancesByOwner[
+ makerAddressLeft
+ ][makerAssetAddressLeft].minus(expectedTransferAmounts.amountSoldByLeftMaker);
+ // Right Maker
+ expectedNewERC20BalancesByOwner[makerAddressRight][
+ takerAssetAddressRight
+ ] = expectedNewERC20BalancesByOwner[makerAddressRight][takerAssetAddressRight].add(
+ expectedTransferAmounts.amountReceivedByRightMaker,
+ );
+ // Taker
+ expectedNewERC20BalancesByOwner[takerAddress][makerAssetAddressLeft] = expectedNewERC20BalancesByOwner[
+ takerAddress
+ ][makerAssetAddressLeft].add(expectedTransferAmounts.amountReceivedByTaker);
+ } else if (makerAssetProxyIdLeft === AssetProxyId.ERC721) {
+ // Decode asset data
+ const erc721AssetData = assetProxyUtils.decodeERC721AssetData(signedOrderLeft.makerAssetData);
+ const makerAssetAddressLeft = erc721AssetData.tokenAddress;
+ const makerAssetIdLeft = erc721AssetData.tokenId;
+ const takerAssetAddressRight = makerAssetAddressLeft;
+ const takerAssetIdRight = makerAssetIdLeft;
+ // Left Maker
+ _.remove(expectedNewERC721TokenIdsByOwner[makerAddressLeft][makerAssetAddressLeft], makerAssetIdLeft);
+ // Right Maker
+ expectedNewERC721TokenIdsByOwner[makerAddressRight][takerAssetAddressRight].push(takerAssetIdRight);
+ // Taker: Since there is only 1 asset transferred, the taker does not receive any of the left maker asset.
+ }
+ // Left Taker Asset (Right Maker Asset)
+ // Note: This exchange is only between the order makers: the Taker does not receive any of the left taker asset.
+ const takerAssetProxyIdLeft = assetProxyUtils.decodeAssetDataId(signedOrderLeft.takerAssetData);
+ if (takerAssetProxyIdLeft === AssetProxyId.ERC20) {
+ // Decode asset data
+ const erc20AssetData = assetProxyUtils.decodeERC20AssetData(signedOrderLeft.takerAssetData);
+ const takerAssetAddressLeft = erc20AssetData.tokenAddress;
+ const makerAssetAddressRight = takerAssetAddressLeft;
+ // Left Maker
+ expectedNewERC20BalancesByOwner[makerAddressLeft][takerAssetAddressLeft] = expectedNewERC20BalancesByOwner[
+ makerAddressLeft
+ ][takerAssetAddressLeft].add(expectedTransferAmounts.amountReceivedByLeftMaker);
+ // Right Maker
+ expectedNewERC20BalancesByOwner[makerAddressRight][
+ makerAssetAddressRight
+ ] = expectedNewERC20BalancesByOwner[makerAddressRight][makerAssetAddressRight].minus(
+ expectedTransferAmounts.amountSoldByRightMaker,
+ );
+ } else if (takerAssetProxyIdLeft === AssetProxyId.ERC721) {
+ // Decode asset data
+ const erc721AssetData = assetProxyUtils.decodeERC721AssetData(signedOrderRight.makerAssetData);
+ const makerAssetAddressRight = erc721AssetData.tokenAddress;
+ const makerAssetIdRight = erc721AssetData.tokenId;
+ const takerAssetAddressLeft = makerAssetAddressRight;
+ const takerAssetIdLeft = makerAssetIdRight;
+ // Right Maker
+ _.remove(expectedNewERC721TokenIdsByOwner[makerAddressRight][makerAssetAddressRight], makerAssetIdRight);
+ // Left Maker
+ expectedNewERC721TokenIdsByOwner[makerAddressLeft][takerAssetAddressLeft].push(takerAssetIdLeft);
+ }
+ // Left Maker Fees
+ expectedNewERC20BalancesByOwner[makerAddressLeft][this._feeTokenAddress] = expectedNewERC20BalancesByOwner[
+ makerAddressLeft
+ ][this._feeTokenAddress].minus(expectedTransferAmounts.feePaidByLeftMaker);
+ // Right Maker Fees
+ expectedNewERC20BalancesByOwner[makerAddressRight][this._feeTokenAddress] = expectedNewERC20BalancesByOwner[
+ makerAddressRight
+ ][this._feeTokenAddress].minus(expectedTransferAmounts.feePaidByRightMaker);
+ // Taker Fees
+ expectedNewERC20BalancesByOwner[takerAddress][this._feeTokenAddress] = expectedNewERC20BalancesByOwner[
+ takerAddress
+ ][this._feeTokenAddress].minus(expectedTransferAmounts.totalFeePaidByTaker);
+ // Left Fee Recipient Fees
+ expectedNewERC20BalancesByOwner[feeRecipientAddressLeft][
+ this._feeTokenAddress
+ ] = expectedNewERC20BalancesByOwner[feeRecipientAddressLeft][this._feeTokenAddress].add(
+ expectedTransferAmounts.feeReceivedLeft,
+ );
+ // Right Fee Recipient Fees
+ expectedNewERC20BalancesByOwner[feeRecipientAddressRight][
+ this._feeTokenAddress
+ ] = expectedNewERC20BalancesByOwner[feeRecipientAddressRight][this._feeTokenAddress].add(
+ expectedTransferAmounts.feeReceivedRight,
+ );
+
+ return [expectedNewERC20BalancesByOwner, expectedNewERC721TokenIdsByOwner];
+ }
+}