@0x:contracts-integrations
Made an initial refactor of MatchOrderTester
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,15 @@
|
||||
import { ERC1155ProxyWrapper, ERC20Wrapper, ERC721Wrapper } from '@0x/contracts-asset-proxy';
|
||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||
import { constants, ERC1155HoldingsByOwner, expect, orderHashUtils, OrderStatus } from '@0x/contracts-test-utils';
|
||||
import { BlockchainBalanceStore, ExchangeContract, LocalBalanceStore } from '@0x/contracts-exchange';
|
||||
import { constants, ERC1155HoldingsByOwner, expect, OrderStatus } from '@0x/contracts-test-utils';
|
||||
import { orderHashUtils } from '@0x/order-utils';
|
||||
import { AssetProxyId, BatchMatchedFillResults, FillResults, MatchedFillResults, SignedOrder } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { LogWithDecodedArgs, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { DeploymentManager } from './deployment_manager';
|
||||
|
||||
const ZERO = new BigNumber(0);
|
||||
|
||||
export interface IndividualERC1155Holdings {
|
||||
@@ -47,7 +51,6 @@ export interface MatchTransferAmounts {
|
||||
export interface MatchResults {
|
||||
orders: MatchedOrders;
|
||||
fills: FillEventArgs[];
|
||||
balances: TokenBalances;
|
||||
}
|
||||
|
||||
export interface BatchMatchResults {
|
||||
@@ -96,48 +99,22 @@ export interface MatchedOrders {
|
||||
rightOrderTakerAssetFilledAmount?: BigNumber;
|
||||
}
|
||||
|
||||
export type BatchMatchOrdersAsyncCall = (
|
||||
leftOrders: SignedOrder[],
|
||||
rightOrders: SignedOrder[],
|
||||
takerAddress: string,
|
||||
) => Promise<TransactionReceiptWithDecodedLogs>;
|
||||
|
||||
export type MatchOrdersAsyncCall = (
|
||||
leftOrder: SignedOrder,
|
||||
rightOrder: SignedOrder,
|
||||
takerAddress: string,
|
||||
) => Promise<TransactionReceiptWithDecodedLogs>;
|
||||
|
||||
export class MatchOrderTester {
|
||||
private readonly _initialTokenBalancesPromise: Promise<TokenBalances>;
|
||||
// FIXME - Should this be public
|
||||
public deployment: DeploymentManager;
|
||||
protected _devUtils: DevUtilsContract;
|
||||
private readonly _blockchainBalanceStore: BlockchainBalanceStore;
|
||||
private readonly _localBalanceStore: LocalBalanceStore;
|
||||
|
||||
/**
|
||||
* 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 erc1155Wrapper Used to fetch ERC1155 token owners.
|
||||
* @param batchMatchOrdersCallAsync Optional, custom caller for
|
||||
* `ExchangeWrapper.batchMatchOrdersAsync()`.
|
||||
* @param batchMatchOrdersWithMaximalFillCallAsync Optional, custom caller for
|
||||
* `ExchangeWrapper.batchMatchOrdersAsync()`.
|
||||
* @param matchOrdersCallAsync Optional, custom caller for
|
||||
* `ExchangeWrapper.matchOrdersAsync()`.
|
||||
* @param matchOrdersWithMaximalFillCallAsync Optional, custom caller for
|
||||
* `ExchangeWrapper.matchOrdersAsync()`.
|
||||
*/
|
||||
constructor(
|
||||
public exchangeWrapper: ExchangeWrapper,
|
||||
public erc20Wrapper: ERC20Wrapper,
|
||||
public erc721Wrapper: ERC721Wrapper,
|
||||
public erc1155ProxyWrapper: ERC1155ProxyWrapper,
|
||||
protected _devUtils: DevUtilsContract,
|
||||
public batchMatchOrdersCallAsync?: BatchMatchOrdersAsyncCall,
|
||||
public batchMatchOrdersWithMaximalFillCallAsync?: BatchMatchOrdersAsyncCall,
|
||||
public matchOrdersCallAsync?: MatchOrdersAsyncCall,
|
||||
public matchOrdersWithMaximalFillCallAsync?: MatchOrdersAsyncCall,
|
||||
deployment: DeploymentManager,
|
||||
devUtils: DevUtilsContract,
|
||||
blockchainBalanceStore: BlockchainBalanceStore,
|
||||
) {
|
||||
this._initialTokenBalancesPromise = this.getBalancesAsync();
|
||||
this.deployment = deployment;
|
||||
this._devUtils = devUtils;
|
||||
this._blockchainBalanceStore = blockchainBalanceStore;
|
||||
this._localBalanceStore = LocalBalanceStore.create(blockchainBalanceStore);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,56 +142,68 @@ export class MatchOrderTester {
|
||||
expect(matchPairs.length).to.be.eq(expectedTransferAmounts.length);
|
||||
expect(orders.leftOrders.length).to.be.eq(orders.leftOrdersTakerAssetFilledAmounts.length);
|
||||
expect(orders.rightOrders.length).to.be.eq(orders.rightOrdersTakerAssetFilledAmounts.length);
|
||||
|
||||
// Ensure that the exchange is in the expected state.
|
||||
await assertBatchOrderStatesAsync(orders, this.exchangeWrapper);
|
||||
await assertBatchOrderStatesAsync(orders, this.deployment.exchange);
|
||||
|
||||
// Get the token balances before executing `batchMatchOrders()`.
|
||||
const _initialTokenBalances = initialTokenBalances
|
||||
? initialTokenBalances
|
||||
: await this._initialTokenBalancesPromise;
|
||||
await this._blockchainBalanceStore.updateBalancesAsync();
|
||||
|
||||
// Execute `batchMatchOrders()`
|
||||
let actualBatchMatchResults;
|
||||
let transactionReceipt;
|
||||
if (withMaximalFill) {
|
||||
actualBatchMatchResults = await this.exchangeWrapper.getBatchMatchOrdersWithMaximalFillResultsAsync(
|
||||
actualBatchMatchResults = await this.deployment.exchange.batchMatchOrdersWithMaximalFill.callAsync(
|
||||
orders.leftOrders,
|
||||
orders.rightOrders,
|
||||
takerAddress,
|
||||
orders.leftOrders.map(order => order.signature),
|
||||
orders.rightOrders.map(order => order.signature),
|
||||
{ from: takerAddress },
|
||||
);
|
||||
transactionReceipt = await this._executeBatchMatchOrdersWithMaximalFillAsync(
|
||||
transactionReceipt = await this.deployment.exchange.batchMatchOrdersWithMaximalFill.awaitTransactionSuccessAsync(
|
||||
orders.leftOrders,
|
||||
orders.rightOrders,
|
||||
takerAddress,
|
||||
orders.leftOrders.map(order => order.signature),
|
||||
orders.rightOrders.map(order => order.signature),
|
||||
{ from: takerAddress },
|
||||
);
|
||||
} else {
|
||||
actualBatchMatchResults = await this.exchangeWrapper.getBatchMatchOrdersResultsAsync(
|
||||
actualBatchMatchResults = await this.deployment.exchange.batchMatchOrders.callAsync(
|
||||
orders.leftOrders,
|
||||
orders.rightOrders,
|
||||
takerAddress,
|
||||
orders.leftOrders.map(order => order.signature),
|
||||
orders.rightOrders.map(order => order.signature),
|
||||
{ from: takerAddress },
|
||||
);
|
||||
transactionReceipt = await this._executeBatchMatchOrdersAsync(
|
||||
transactionReceipt = await this.deployment.exchange.batchMatchOrders.awaitTransactionSuccessAsync(
|
||||
orders.leftOrders,
|
||||
orders.rightOrders,
|
||||
takerAddress,
|
||||
orders.leftOrders.map(order => order.signature),
|
||||
orders.rightOrders.map(order => order.signature),
|
||||
{ from: takerAddress },
|
||||
);
|
||||
}
|
||||
|
||||
// Simulate the batch order match.
|
||||
const expectedBatchMatchResults = await simulateBatchMatchOrdersAsync(
|
||||
orders,
|
||||
takerAddress,
|
||||
_initialTokenBalances,
|
||||
this._blockchainBalanceStore,
|
||||
this._localBalanceStore,
|
||||
matchPairs,
|
||||
expectedTransferAmounts,
|
||||
this._devUtils,
|
||||
);
|
||||
const expectedResults = convertToBatchMatchResults(expectedBatchMatchResults);
|
||||
expect(actualBatchMatchResults).to.be.eql(expectedResults);
|
||||
|
||||
// Validate the simulation against reality.
|
||||
await assertBatchMatchResultsAsync(
|
||||
expectedBatchMatchResults,
|
||||
transactionReceipt,
|
||||
await this.getBalancesAsync(),
|
||||
_initialTokenBalances,
|
||||
this.exchangeWrapper,
|
||||
this._blockchainBalanceStore,
|
||||
this._localBalanceStore,
|
||||
this.deployment.exchange,
|
||||
);
|
||||
return expectedBatchMatchResults;
|
||||
}
|
||||
@@ -237,108 +226,65 @@ export class MatchOrderTester {
|
||||
withMaximalFill: boolean,
|
||||
initialTokenBalances?: TokenBalances,
|
||||
): Promise<MatchResults> {
|
||||
await assertInitialOrderStatesAsync(orders, this.exchangeWrapper);
|
||||
// Get the token balances before executing `matchOrders()`.
|
||||
const _initialTokenBalances = initialTokenBalances
|
||||
? initialTokenBalances
|
||||
: await this._initialTokenBalancesPromise;
|
||||
await assertInitialOrderStatesAsync(orders, this.deployment.exchange);
|
||||
|
||||
// Execute `matchOrders()`
|
||||
let actualMatchResults;
|
||||
let transactionReceipt;
|
||||
if (withMaximalFill) {
|
||||
actualMatchResults = await this.exchangeWrapper.getMatchOrdersWithMaximalFillResultsAsync(
|
||||
actualMatchResults = await this.deployment.exchange.matchOrdersWithMaximalFill.callAsync(
|
||||
orders.leftOrder,
|
||||
orders.rightOrder,
|
||||
takerAddress,
|
||||
{},
|
||||
orders.leftOrder.signature,
|
||||
orders.rightOrder.signature,
|
||||
{ from: takerAddress },
|
||||
);
|
||||
transactionReceipt = await this._executeMatchOrdersWithMaximalFillAsync(
|
||||
transactionReceipt = await this.deployment.exchange.matchOrdersWithMaximalFill.awaitTransactionSuccessAsync(
|
||||
orders.leftOrder,
|
||||
orders.rightOrder,
|
||||
takerAddress,
|
||||
orders.leftOrder.signature,
|
||||
orders.rightOrder.signature,
|
||||
{ from: takerAddress },
|
||||
);
|
||||
} else {
|
||||
actualMatchResults = await this.exchangeWrapper.getMatchOrdersResultsAsync(
|
||||
actualMatchResults = await this.deployment.exchange.matchOrders.callAsync(
|
||||
orders.leftOrder,
|
||||
orders.rightOrder,
|
||||
takerAddress,
|
||||
orders.leftOrder.signature,
|
||||
orders.rightOrder.signature,
|
||||
{ from: takerAddress },
|
||||
);
|
||||
transactionReceipt = await this.deployment.exchange.matchOrders.awaitTransactionSuccessAsync(
|
||||
orders.leftOrder,
|
||||
orders.rightOrder,
|
||||
orders.leftOrder.signature,
|
||||
orders.rightOrder.signature,
|
||||
{ from: takerAddress },
|
||||
);
|
||||
transactionReceipt = await this._executeMatchOrdersAsync(orders.leftOrder, orders.rightOrder, takerAddress);
|
||||
}
|
||||
|
||||
// Simulate the fill.
|
||||
const expectedMatchResults = await simulateMatchOrdersAsync(
|
||||
orders,
|
||||
takerAddress,
|
||||
_initialTokenBalances,
|
||||
this._blockchainBalanceStore,
|
||||
toFullMatchTransferAmounts(expectedTransferAmounts),
|
||||
this._devUtils,
|
||||
this._localBalanceStore,
|
||||
);
|
||||
const expectedResults = convertToMatchResults(expectedMatchResults);
|
||||
expect(actualMatchResults).to.be.eql(expectedResults);
|
||||
|
||||
// Validate the simulation against reality.
|
||||
await assertMatchResultsAsync(
|
||||
expectedMatchResults,
|
||||
transactionReceipt,
|
||||
await this.getBalancesAsync(),
|
||||
this.exchangeWrapper,
|
||||
this._localBalanceStore,
|
||||
this._blockchainBalanceStore,
|
||||
this.deployment.exchange,
|
||||
);
|
||||
return expectedMatchResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the current token balances of all known accounts.
|
||||
*/
|
||||
public async getBalancesAsync(): Promise<TokenBalances> {
|
||||
return getTokenBalancesAsync(this.erc20Wrapper, this.erc721Wrapper, this.erc1155ProxyWrapper);
|
||||
}
|
||||
|
||||
private async _executeBatchMatchOrdersAsync(
|
||||
leftOrders: SignedOrder[],
|
||||
rightOrders: SignedOrder[],
|
||||
takerAddress: string,
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const caller =
|
||||
this.batchMatchOrdersCallAsync ||
|
||||
(async (_leftOrders: SignedOrder[], _rightOrders: SignedOrder[], _takerAddress: string) =>
|
||||
this.exchangeWrapper.batchMatchOrdersAsync(_leftOrders, _rightOrders, _takerAddress));
|
||||
return caller(leftOrders, rightOrders, takerAddress);
|
||||
}
|
||||
|
||||
private async _executeBatchMatchOrdersWithMaximalFillAsync(
|
||||
leftOrders: SignedOrder[],
|
||||
rightOrders: SignedOrder[],
|
||||
takerAddress: string,
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const caller =
|
||||
this.batchMatchOrdersWithMaximalFillCallAsync ||
|
||||
(async (_leftOrders: SignedOrder[], _rightOrders: SignedOrder[], _takerAddress: string) =>
|
||||
this.exchangeWrapper.batchMatchOrdersWithMaximalFillAsync(_leftOrders, _rightOrders, _takerAddress));
|
||||
return caller(leftOrders, rightOrders, takerAddress);
|
||||
}
|
||||
|
||||
private async _executeMatchOrdersAsync(
|
||||
leftOrder: SignedOrder,
|
||||
rightOrder: SignedOrder,
|
||||
takerAddress: string,
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const caller =
|
||||
this.matchOrdersCallAsync ||
|
||||
(async (_leftOrder: SignedOrder, _rightOrder: SignedOrder, _takerAddress: string) =>
|
||||
this.exchangeWrapper.matchOrdersAsync(_leftOrder, _rightOrder, _takerAddress));
|
||||
return caller(leftOrder, rightOrder, takerAddress);
|
||||
}
|
||||
|
||||
private async _executeMatchOrdersWithMaximalFillAsync(
|
||||
leftOrder: SignedOrder,
|
||||
rightOrder: SignedOrder,
|
||||
takerAddress: string,
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const caller =
|
||||
this.matchOrdersWithMaximalFillCallAsync ||
|
||||
(async (_leftOrder: SignedOrder, _rightOrder: SignedOrder, _takerAddress: string) =>
|
||||
this.exchangeWrapper.matchOrdersWithMaximalFillAsync(_leftOrder, _rightOrder, _takerAddress));
|
||||
return caller(leftOrder, rightOrder, takerAddress);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -360,8 +306,7 @@ function toFullMatchTransferAmounts(partial: Partial<MatchTransferAmounts>): Mat
|
||||
partial.rightMakerAssetBoughtByLeftMakerAmount ||
|
||||
partial.rightMakerAssetSoldByRightMakerAmount ||
|
||||
ZERO,
|
||||
leftMakerAssetBoughtByRightMakerAmount:
|
||||
partial.leftMakerAssetBoughtByRightMakerAmount ||
|
||||
leftMakerAssetBoughtByRightMakerAmount: partial.leftMakerAssetBoughtByRightMakerAmount ||
|
||||
partial.leftMakerAssetSoldByLeftMakerAmount ||
|
||||
ZERO,
|
||||
leftMakerFeeAssetPaidByLeftMakerAmount:
|
||||
@@ -391,7 +336,8 @@ function toFullMatchTransferAmounts(partial: Partial<MatchTransferAmounts>): Mat
|
||||
async function simulateBatchMatchOrdersAsync(
|
||||
orders: BatchMatchedOrders,
|
||||
takerAddress: string,
|
||||
tokenBalances: TokenBalances,
|
||||
blockchainBalanceStore: BlockchainBalanceStore,
|
||||
localBalanceStore: LocalBalanceStore,
|
||||
matchPairs: Array<[number, number]>,
|
||||
transferAmounts: Array<Partial<MatchTransferAmounts>>,
|
||||
devUtils: DevUtilsContract,
|
||||
@@ -449,14 +395,16 @@ async function simulateBatchMatchOrdersAsync(
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME - These arguments should be reordered
|
||||
// Add the latest match to the batch match results
|
||||
batchMatchResults.matches.push(
|
||||
await simulateMatchOrdersAsync(
|
||||
matchedOrders,
|
||||
takerAddress,
|
||||
tokenBalances,
|
||||
blockchainBalanceStore,
|
||||
toFullMatchTransferAmounts(transferAmounts[i]),
|
||||
devUtils,
|
||||
localBalanceStore,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -503,6 +451,7 @@ async function simulateBatchMatchOrdersAsync(
|
||||
return batchMatchResults;
|
||||
}
|
||||
|
||||
// FIXME - Is it possible to remove `transferAmounts`
|
||||
/**
|
||||
* Simulates matching two orders by transferring amounts defined in
|
||||
* `transferAmounts` and returns the results.
|
||||
@@ -515,9 +464,10 @@ async function simulateBatchMatchOrdersAsync(
|
||||
async function simulateMatchOrdersAsync(
|
||||
orders: MatchedOrders,
|
||||
takerAddress: string,
|
||||
tokenBalances: TokenBalances,
|
||||
blockchainBalanceStore: BlockchainBalanceStore, // FIXME - Is this right?
|
||||
transferAmounts: MatchTransferAmounts,
|
||||
devUtils: DevUtilsContract,
|
||||
localBalanceStore: LocalBalanceStore,
|
||||
): Promise<MatchResults> {
|
||||
// prettier-ignore
|
||||
const matchResults = {
|
||||
@@ -534,174 +484,73 @@ async function simulateMatchOrdersAsync(
|
||||
),
|
||||
},
|
||||
fills: simulateFillEvents(orders, takerAddress, transferAmounts),
|
||||
balances: _.cloneDeep(tokenBalances),
|
||||
};
|
||||
// Right maker asset -> left maker
|
||||
await transferAssetAsync(
|
||||
localBalanceStore.transferAsset(
|
||||
orders.rightOrder.makerAddress,
|
||||
orders.leftOrder.makerAddress,
|
||||
transferAmounts.rightMakerAssetBoughtByLeftMakerAmount,
|
||||
orders.rightOrder.makerAssetData,
|
||||
matchResults,
|
||||
devUtils,
|
||||
);
|
||||
// FIXME - Is this a necessary condition?
|
||||
if (orders.leftOrder.makerAddress !== orders.leftOrder.feeRecipientAddress) {
|
||||
// Left maker fees
|
||||
await transferAssetAsync(
|
||||
localBalanceStore.transferAsset(
|
||||
orders.leftOrder.makerAddress,
|
||||
orders.leftOrder.feeRecipientAddress,
|
||||
transferAmounts.leftMakerFeeAssetPaidByLeftMakerAmount,
|
||||
orders.leftOrder.makerFeeAssetData,
|
||||
matchResults,
|
||||
devUtils,
|
||||
);
|
||||
}
|
||||
// Left maker asset -> right maker
|
||||
await transferAssetAsync(
|
||||
localBalanceStore.transferAsset(
|
||||
orders.leftOrder.makerAddress,
|
||||
orders.rightOrder.makerAddress,
|
||||
transferAmounts.leftMakerAssetBoughtByRightMakerAmount,
|
||||
orders.leftOrder.makerAssetData,
|
||||
matchResults,
|
||||
devUtils,
|
||||
);
|
||||
// FIXME - Is this a necessary condition?
|
||||
if (orders.rightOrder.makerAddress !== orders.rightOrder.feeRecipientAddress) {
|
||||
// Right maker fees
|
||||
await transferAssetAsync(
|
||||
localBalanceStore.transferAsset(
|
||||
orders.rightOrder.makerAddress,
|
||||
orders.rightOrder.feeRecipientAddress,
|
||||
transferAmounts.rightMakerFeeAssetPaidByRightMakerAmount,
|
||||
orders.rightOrder.makerFeeAssetData,
|
||||
matchResults,
|
||||
devUtils,
|
||||
);
|
||||
}
|
||||
// Left taker profit
|
||||
await transferAssetAsync(
|
||||
localBalanceStore.transferAsset(
|
||||
orders.leftOrder.makerAddress,
|
||||
takerAddress,
|
||||
transferAmounts.leftMakerAssetReceivedByTakerAmount,
|
||||
orders.leftOrder.makerAssetData,
|
||||
matchResults,
|
||||
devUtils,
|
||||
);
|
||||
// Right taker profit
|
||||
await transferAssetAsync(
|
||||
localBalanceStore.transferAsset(
|
||||
orders.rightOrder.makerAddress,
|
||||
takerAddress,
|
||||
transferAmounts.rightMakerAssetReceivedByTakerAmount,
|
||||
orders.rightOrder.makerAssetData,
|
||||
matchResults,
|
||||
devUtils,
|
||||
);
|
||||
// Left taker fees
|
||||
await transferAssetAsync(
|
||||
localBalanceStore.transferAsset(
|
||||
takerAddress,
|
||||
orders.leftOrder.feeRecipientAddress,
|
||||
transferAmounts.leftTakerFeeAssetPaidByTakerAmount,
|
||||
orders.leftOrder.takerFeeAssetData,
|
||||
matchResults,
|
||||
devUtils,
|
||||
);
|
||||
// Right taker fees
|
||||
await transferAssetAsync(
|
||||
localBalanceStore.transferAsset(
|
||||
takerAddress,
|
||||
orders.rightOrder.feeRecipientAddress,
|
||||
transferAmounts.rightTakerFeeAssetPaidByTakerAmount,
|
||||
orders.rightOrder.takerFeeAssetData,
|
||||
matchResults,
|
||||
devUtils,
|
||||
);
|
||||
|
||||
return matchResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates a transfer of assets from `fromAddress` to `toAddress`
|
||||
* by updating `matchResults`.
|
||||
*/
|
||||
async function transferAssetAsync(
|
||||
fromAddress: string,
|
||||
toAddress: string,
|
||||
amount: BigNumber,
|
||||
assetData: string,
|
||||
matchResults: MatchResults,
|
||||
devUtils: DevUtilsContract,
|
||||
): Promise<void> {
|
||||
const assetProxyId = await devUtils.decodeAssetProxyId(assetData).callAsync();
|
||||
switch (assetProxyId) {
|
||||
case AssetProxyId.ERC20: {
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
const [proxyId, assetAddress] = await devUtils.decodeERC20AssetData(assetData).callAsync(); // tslint:disable-line-no-unused-variable
|
||||
const fromBalances = matchResults.balances.erc20[fromAddress];
|
||||
const toBalances = matchResults.balances.erc20[toAddress];
|
||||
fromBalances[assetAddress] = fromBalances[assetAddress].minus(amount);
|
||||
toBalances[assetAddress] = toBalances[assetAddress].plus(amount);
|
||||
break;
|
||||
}
|
||||
case AssetProxyId.ERC721: {
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
const [proxyId, assetAddress, tokenId] = await devUtils.decodeERC721AssetData(assetData).callAsync(); // tslint:disable-line-no-unused-variable
|
||||
const fromTokens = matchResults.balances.erc721[fromAddress][assetAddress];
|
||||
const toTokens = matchResults.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: {
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
const [proxyId, assetAddress, tokenIds, tokenValues] = await devUtils
|
||||
.decodeERC1155AssetData(assetData)
|
||||
.callAsync();
|
||||
const fromBalances = matchResults.balances.erc1155[fromAddress][assetAddress];
|
||||
const toBalances = matchResults.balances.erc1155[toAddress][assetAddress];
|
||||
for (const i of _.times(tokenIds.length)) {
|
||||
const tokenId = tokenIds[i];
|
||||
const tokenValue = 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AssetProxyId.MultiAsset: {
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
const [proxyId, amounts, nestedAssetData] = await devUtils.decodeMultiAssetData(assetData).callAsync(); // tslint:disable-line-no-unused-variable
|
||||
for (const i of _.times(amounts.length)) {
|
||||
const nestedAmount = amount.times(amounts[i]);
|
||||
const _nestedAssetData = nestedAssetData[i];
|
||||
await transferAssetAsync(
|
||||
fromAddress,
|
||||
toAddress,
|
||||
nestedAmount,
|
||||
_nestedAssetData,
|
||||
matchResults,
|
||||
devUtils,
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unhandled asset proxy ID: ${assetProxyId}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the results of `simulateBatchMatchOrders()` agrees with reality.
|
||||
* @param batchMatchResults The results of a `simulateBatchMatchOrders()`.
|
||||
@@ -712,22 +561,24 @@ async function transferAssetAsync(
|
||||
async function assertBatchMatchResultsAsync(
|
||||
batchMatchResults: BatchMatchResults,
|
||||
transactionReceipt: TransactionReceiptWithDecodedLogs,
|
||||
actualTokenBalances: TokenBalances,
|
||||
initialTokenBalances: TokenBalances,
|
||||
exchangeWrapper: ExchangeWrapper,
|
||||
blockchainBalanceStore: BlockchainBalanceStore,
|
||||
localBalanceStore: LocalBalanceStore,
|
||||
exchange: ExchangeContract,
|
||||
): Promise<void> {
|
||||
// Ensure that the batchMatchResults contain at least one match
|
||||
expect(batchMatchResults.matches.length).to.be.gt(0);
|
||||
|
||||
// Check the fill events.
|
||||
assertFillEvents(
|
||||
batchMatchResults.matches.map(match => match.fills).reduce((total, fills) => total.concat(fills)),
|
||||
transactionReceipt,
|
||||
);
|
||||
// Check the token balances.
|
||||
const newBalances = getUpdatedBalances(batchMatchResults, initialTokenBalances);
|
||||
assertBalances(newBalances, actualTokenBalances);
|
||||
|
||||
// Ensure that the actual and expected token balances are equivalent.
|
||||
localBalanceStore.assertEquals(blockchainBalanceStore);
|
||||
|
||||
// Check the Exchange state.
|
||||
await assertPostBatchExchangeStateAsync(batchMatchResults, exchangeWrapper);
|
||||
await assertPostBatchExchangeStateAsync(batchMatchResults, exchange);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -740,15 +591,18 @@ async function assertBatchMatchResultsAsync(
|
||||
async function assertMatchResultsAsync(
|
||||
matchResults: MatchResults,
|
||||
transactionReceipt: TransactionReceiptWithDecodedLogs,
|
||||
actualTokenBalances: TokenBalances,
|
||||
exchangeWrapper: ExchangeWrapper,
|
||||
localBalanceStore: LocalBalanceStore,
|
||||
blockchainBalanceStore: BlockchainBalanceStore,
|
||||
exchange: ExchangeContract,
|
||||
): Promise<void> {
|
||||
// Check the fill events.
|
||||
assertFillEvents(matchResults.fills, transactionReceipt);
|
||||
|
||||
// Check the token balances.
|
||||
assertBalances(matchResults.balances, actualTokenBalances);
|
||||
localBalanceStore.assertEquals(blockchainBalanceStore);
|
||||
|
||||
// Check the Exchange state.
|
||||
await assertPostExchangeStateAsync(matchResults, exchangeWrapper);
|
||||
await assertPostExchangeStateAsync(matchResults, exchange);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -856,16 +710,13 @@ function assertBalances(expectedBalances: TokenBalances, actualBalances: TokenBa
|
||||
* @param orders Batch matched orders with intial filled amounts.
|
||||
* @param exchangeWrapper ExchangeWrapper instance.
|
||||
*/
|
||||
async function assertBatchOrderStatesAsync(
|
||||
orders: BatchMatchedOrders,
|
||||
exchangeWrapper: ExchangeWrapper,
|
||||
): Promise<void> {
|
||||
async function assertBatchOrderStatesAsync(orders: BatchMatchedOrders, exchange: ExchangeContract): Promise<void> {
|
||||
for (let i = 0; i < orders.leftOrders.length; i++) {
|
||||
await assertOrderFilledAmountAsync(
|
||||
orders.leftOrders[i],
|
||||
orders.leftOrdersTakerAssetFilledAmounts[i],
|
||||
'left',
|
||||
exchangeWrapper,
|
||||
exchange,
|
||||
);
|
||||
}
|
||||
for (let i = 0; i < orders.rightOrders.length; i++) {
|
||||
@@ -873,7 +724,7 @@ async function assertBatchOrderStatesAsync(
|
||||
orders.rightOrders[i],
|
||||
orders.rightOrdersTakerAssetFilledAmounts[i],
|
||||
'right',
|
||||
exchangeWrapper,
|
||||
exchange,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -883,7 +734,7 @@ async function assertBatchOrderStatesAsync(
|
||||
* @param orders Matched orders with intial filled amounts.
|
||||
* @param exchangeWrapper ExchangeWrapper instance.
|
||||
*/
|
||||
async function assertInitialOrderStatesAsync(orders: MatchedOrders, exchangeWrapper: ExchangeWrapper): Promise<void> {
|
||||
async function assertInitialOrderStatesAsync(orders: MatchedOrders, exchange: ExchangeContract): Promise<void> {
|
||||
const pairs = [
|
||||
[orders.leftOrder, orders.leftOrderTakerAssetFilledAmount || ZERO],
|
||||
[orders.rightOrder, orders.rightOrderTakerAssetFilledAmount || ZERO],
|
||||
@@ -891,7 +742,7 @@ async function assertInitialOrderStatesAsync(orders: MatchedOrders, exchangeWrap
|
||||
await Promise.all(
|
||||
pairs.map(async ([order, expectedFilledAmount]) => {
|
||||
const side = order === orders.leftOrder ? 'left' : 'right';
|
||||
await assertOrderFilledAmountAsync(order, expectedFilledAmount, side, exchangeWrapper);
|
||||
await assertOrderFilledAmountAsync(order, expectedFilledAmount, side, exchange);
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -903,9 +754,9 @@ async function assertInitialOrderStatesAsync(orders: MatchedOrders, exchangeWrap
|
||||
*/
|
||||
async function assertPostBatchExchangeStateAsync(
|
||||
batchMatchResults: BatchMatchResults,
|
||||
exchangeWrapper: ExchangeWrapper,
|
||||
exchange: ExchangeContract,
|
||||
): Promise<void> {
|
||||
await assertTriplesExchangeStateAsync(batchMatchResults.filledAmounts, exchangeWrapper);
|
||||
await assertTriplesExchangeStateAsync(batchMatchResults.filledAmounts, exchange);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -913,15 +764,12 @@ async function assertPostBatchExchangeStateAsync(
|
||||
* @param matchResults Results from a call to `simulateMatchOrders()`.
|
||||
* @param exchangeWrapper The ExchangeWrapper instance.
|
||||
*/
|
||||
async function assertPostExchangeStateAsync(
|
||||
matchResults: MatchResults,
|
||||
exchangeWrapper: ExchangeWrapper,
|
||||
): Promise<void> {
|
||||
async function assertPostExchangeStateAsync(matchResults: MatchResults, exchange: ExchangeContract): Promise<void> {
|
||||
const triples = [
|
||||
[matchResults.orders.leftOrder, matchResults.orders.leftOrderTakerAssetFilledAmount, 'left'],
|
||||
[matchResults.orders.rightOrder, matchResults.orders.rightOrderTakerAssetFilledAmount, 'right'],
|
||||
] as Array<[SignedOrder, BigNumber, string]>;
|
||||
await assertTriplesExchangeStateAsync(triples, exchangeWrapper);
|
||||
await assertTriplesExchangeStateAsync(triples, exchange);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -934,12 +782,12 @@ async function assertPostExchangeStateAsync(
|
||||
*/
|
||||
async function assertTriplesExchangeStateAsync(
|
||||
triples: Array<[SignedOrder, BigNumber, string]>,
|
||||
exchangeWrapper: ExchangeWrapper,
|
||||
exchange: ExchangeContract,
|
||||
): Promise<void> {
|
||||
await Promise.all(
|
||||
triples.map(async ([order, expectedFilledAmount, side]) => {
|
||||
expect(['left', 'right']).to.include(side);
|
||||
await assertOrderFilledAmountAsync(order, expectedFilledAmount, side, exchangeWrapper);
|
||||
await assertOrderFilledAmountAsync(order, expectedFilledAmount, side, exchange);
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -951,15 +799,15 @@ async function assertTriplesExchangeStateAsync(
|
||||
* @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.
|
||||
* @param exchange The exchange contract that is in the current deployment.
|
||||
*/
|
||||
async function assertOrderFilledAmountAsync(
|
||||
order: SignedOrder,
|
||||
expectedFilledAmount: BigNumber,
|
||||
side: string,
|
||||
exchangeWrapper: ExchangeWrapper,
|
||||
exchange: ExchangeContract,
|
||||
): Promise<void> {
|
||||
const orderInfo = await exchangeWrapper.getOrderInfoAsync(order);
|
||||
const orderInfo = await exchange.getOrderInfo.callAsync(order);
|
||||
// Check filled amount of order.
|
||||
const actualFilledAmount = orderInfo.orderTakerAssetFilledAmount;
|
||||
expect(actualFilledAmount, `${side} order final filled amount`).to.be.bignumber.equal(expectedFilledAmount);
|
||||
@@ -1038,17 +886,6 @@ function getLastMatch(batchMatchResults: BatchMatchResults): MatchResults {
|
||||
return batchMatchResults.matches[batchMatchResults.matches.length - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token balances
|
||||
* @param batchMatchResults The results of a batch order match
|
||||
* @return The token balances results from after the batch
|
||||
*/
|
||||
function getUpdatedBalances(batchMatchResults: BatchMatchResults, initialTokenBalances: TokenBalances): TokenBalances {
|
||||
return batchMatchResults.matches
|
||||
.map(match => match.balances)
|
||||
.reduce((totalBalances, balances) => aggregateBalances(totalBalances, balances, initialTokenBalances));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new fill results object to a total fill results object destructively.
|
||||
* @param total The total fill results that should be updated.
|
||||
|
Reference in New Issue
Block a user