Created tests for batchMatchOrders
This commit is contained in:
parent
f289b3112b
commit
6cf11554de
@ -57,20 +57,23 @@ contract MixinMatchOrders is
|
|||||||
require(leftOrders.length == leftSignatures.length, "Incompatible leftOrders and leftSignatures");
|
require(leftOrders.length == leftSignatures.length, "Incompatible leftOrders and leftSignatures");
|
||||||
require(rightOrders.length == rightSignatures.length, "Incompatible rightOrders and rightSignatures");
|
require(rightOrders.length == rightSignatures.length, "Incompatible rightOrders and rightSignatures");
|
||||||
|
|
||||||
uint256 minLength = _min256(leftOrders.length, rightOrders.length);
|
// Without simulating all of the order matching, this program cannot know how many
|
||||||
|
// matches there will be. To ensure that batchMatchedFillResults has enough memory
|
||||||
batchMatchedFillResults.left = new LibFillResults.FillResults[](minLength);
|
// allocated for the left and the right side, we will allocate enough space for the
|
||||||
batchMatchedFillResults.right = new LibFillResults.FillResults[](minLength);
|
// maximum amount of matches (the maximum of the left and the right sides).
|
||||||
|
uint256 maxLength = _max256(leftOrders.length, rightOrders.length);
|
||||||
|
batchMatchedFillResults.left = new LibFillResults.FillResults[](maxLength);
|
||||||
|
batchMatchedFillResults.right = new LibFillResults.FillResults[](maxLength);
|
||||||
|
|
||||||
// Initialize initial variables
|
// Initialize initial variables
|
||||||
uint256 matchCount;
|
uint matchCount;
|
||||||
uint256 leftIdx = 0;
|
uint256 leftIdx = 0;
|
||||||
uint256 rightIdx = 0;
|
uint256 rightIdx = 0;
|
||||||
|
|
||||||
LibOrder.Order memory leftOrder = leftOrders[0];
|
LibOrder.Order memory leftOrder = leftOrders[0];
|
||||||
LibOrder.Order memory rightOrder = rightOrders[0];
|
LibOrder.Order memory rightOrder = rightOrders[0];
|
||||||
bytes memory leftSignature = leftSignatures[0];
|
bytes memory leftSignature = leftSignatures[0];
|
||||||
bytes memory rightSignature = leftSignatures[0];
|
bytes memory rightSignature = rightSignatures[0];
|
||||||
|
|
||||||
|
|
||||||
// Loop infinitely (until broken inside of the loop), but keep a counter of how
|
// Loop infinitely (until broken inside of the loop), but keep a counter of how
|
||||||
// many orders have been matched.
|
// many orders have been matched.
|
||||||
@ -93,6 +96,7 @@ contract MixinMatchOrders is
|
|||||||
);
|
);
|
||||||
// batchMatchedFillResults.profitInRightMakerAsset += 0; // Placeholder for ZEIP 40
|
// batchMatchedFillResults.profitInRightMakerAsset += 0; // Placeholder for ZEIP 40
|
||||||
|
|
||||||
|
|
||||||
// If the leftOrder is filled, update the leftIdx, leftOrder, and leftSignature,
|
// If the leftOrder is filled, update the leftIdx, leftOrder, and leftSignature,
|
||||||
// or break out of the loop if there are no more leftOrders to match.
|
// or break out of the loop if there are no more leftOrders to match.
|
||||||
if (_isFilled(leftOrder, matchResults.left)) {
|
if (_isFilled(leftOrder, matchResults.left)) {
|
||||||
|
@ -14,7 +14,7 @@ import { DummyERC721TokenContract } from '@0x/contracts-erc721';
|
|||||||
import { chaiSetup, constants, OrderFactory, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
|
import { chaiSetup, constants, OrderFactory, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
|
||||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
import { BlockchainLifecycle } from '@0x/dev-utils';
|
||||||
import { assetDataUtils, ExchangeRevertErrors, orderHashUtils } from '@0x/order-utils';
|
import { assetDataUtils, ExchangeRevertErrors, orderHashUtils } from '@0x/order-utils';
|
||||||
import { OrderStatus } from '@0x/types';
|
import { OrderStatus, SignedOrder } from '@0x/types';
|
||||||
import { BigNumber, providerUtils, ReentrancyGuardRevertErrors } from '@0x/utils';
|
import { BigNumber, providerUtils, ReentrancyGuardRevertErrors } from '@0x/utils';
|
||||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||||
import * as chai from 'chai';
|
import * as chai from 'chai';
|
||||||
@ -31,6 +31,7 @@ import {
|
|||||||
|
|
||||||
import { MatchOrderTester, TokenBalances } from './utils/match_order_tester';
|
import { MatchOrderTester, TokenBalances } from './utils/match_order_tester';
|
||||||
|
|
||||||
|
const ZERO = new BigNumber(0);
|
||||||
const ONE = new BigNumber(1);
|
const ONE = new BigNumber(1);
|
||||||
const TWO = new BigNumber(2);
|
const TWO = new BigNumber(2);
|
||||||
|
|
||||||
@ -1850,5 +1851,193 @@ describe('matchOrders', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe.only('batchMatchOrders', () => {
|
||||||
|
it('should fail if there are zero leftOrders', async () => {
|
||||||
|
const leftOrders: SignedOrder[] = [];
|
||||||
|
const rightOrders = [
|
||||||
|
await orderFactoryRight.newSignedOrderAsync({
|
||||||
|
makerAddress: makerAddressRight,
|
||||||
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
||||||
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 0),
|
||||||
|
feeRecipientAddress: feeRecipientAddressRight,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
const tx = exchangeWrapper.batchMatchOrdersAsync(leftOrders, rightOrders, takerAddress);
|
||||||
|
return expect(tx).to.be.rejected();
|
||||||
|
});
|
||||||
|
it('should fail if there are zero rightOrders', async () => {
|
||||||
|
const leftOrders = [
|
||||||
|
await orderFactoryLeft.newSignedOrderAsync({
|
||||||
|
makerAddress: makerAddressLeft,
|
||||||
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
||||||
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 0),
|
||||||
|
feeRecipientAddress: feeRecipientAddressLeft,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
const rightOrders: SignedOrder[] = [];
|
||||||
|
const tx = exchangeWrapper.batchMatchOrdersAsync(leftOrders, rightOrders, takerAddress);
|
||||||
|
return expect(tx).to.be.rejected();
|
||||||
|
});
|
||||||
|
it('should correctly match two opposite orders', async () => {
|
||||||
|
const leftOrders = [
|
||||||
|
await orderFactoryLeft.newSignedOrderAsync({
|
||||||
|
makerAddress: makerAddressLeft,
|
||||||
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 0),
|
||||||
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
||||||
|
feeRecipientAddress: feeRecipientAddressLeft,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
const rightOrders = [
|
||||||
|
await orderFactoryRight.newSignedOrderAsync({
|
||||||
|
makerAddress: makerAddressRight,
|
||||||
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
||||||
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 0),
|
||||||
|
feeRecipientAddress: feeRecipientAddressRight,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
const expectedTransferAmounts = [
|
||||||
|
{
|
||||||
|
// Left Maker
|
||||||
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 0),
|
||||||
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
||||||
|
// Right Maker
|
||||||
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
||||||
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
||||||
|
// Taker
|
||||||
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
||||||
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
||||||
|
},
|
||||||
|
];
|
||||||
|
await matchOrderTester.batchMatchOrdersAndAssertEffectsAsync(
|
||||||
|
{
|
||||||
|
leftOrders,
|
||||||
|
rightOrders,
|
||||||
|
leftOrdersTakerAssetFilledAmounts: [ZERO],
|
||||||
|
rightOrdersTakerAssetFilledAmounts: [ZERO],
|
||||||
|
},
|
||||||
|
takerAddress,
|
||||||
|
[[0, 0]],
|
||||||
|
expectedTransferAmounts,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('should correctly match two left orders to one complementary right order', async () => {
|
||||||
|
const leftOrders = [
|
||||||
|
await orderFactoryLeft.newSignedOrderAsync({
|
||||||
|
makerAddress: makerAddressLeft,
|
||||||
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 0),
|
||||||
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
||||||
|
feeRecipientAddress: feeRecipientAddressLeft,
|
||||||
|
}),
|
||||||
|
await orderFactoryLeft.newSignedOrderAsync({
|
||||||
|
makerAddress: makerAddressLeft,
|
||||||
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 0),
|
||||||
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
||||||
|
feeRecipientAddress: feeRecipientAddressLeft,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
const rightOrders = [
|
||||||
|
await orderFactoryRight.newSignedOrderAsync({
|
||||||
|
makerAddress: makerAddressRight,
|
||||||
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 0),
|
||||||
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(4, 0),
|
||||||
|
feeRecipientAddress: feeRecipientAddressRight,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
const expectedTransferAmounts = [
|
||||||
|
{
|
||||||
|
// Left Maker
|
||||||
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 0),
|
||||||
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
||||||
|
// Right Maker
|
||||||
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
||||||
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(50, 16), // 50%
|
||||||
|
// Taker
|
||||||
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 50%
|
||||||
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(50, 16), // 50%
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Left Maker
|
||||||
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 0),
|
||||||
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 50%
|
||||||
|
// Right Maker
|
||||||
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
||||||
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(50, 16), // 50%
|
||||||
|
// Taker
|
||||||
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 50%
|
||||||
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(50, 16), // 50%
|
||||||
|
},
|
||||||
|
];
|
||||||
|
await matchOrderTester.batchMatchOrdersAndAssertEffectsAsync(
|
||||||
|
{
|
||||||
|
leftOrders,
|
||||||
|
rightOrders,
|
||||||
|
leftOrdersTakerAssetFilledAmounts: [ZERO, ZERO],
|
||||||
|
rightOrdersTakerAssetFilledAmounts: [ZERO],
|
||||||
|
},
|
||||||
|
takerAddress,
|
||||||
|
[[0, 0], [1, 0]],
|
||||||
|
expectedTransferAmounts,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('should correctly match one left order to two complementary right orders', async () => {
|
||||||
|
const leftOrders = [
|
||||||
|
await orderFactoryLeft.newSignedOrderAsync({
|
||||||
|
makerAddress: makerAddressLeft,
|
||||||
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(4, 0),
|
||||||
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 0),
|
||||||
|
feeRecipientAddress: feeRecipientAddressLeft,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
const rightOrders = [
|
||||||
|
await orderFactoryRight.newSignedOrderAsync({
|
||||||
|
makerAddress: makerAddressRight,
|
||||||
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
||||||
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 0),
|
||||||
|
feeRecipientAddress: feeRecipientAddressRight,
|
||||||
|
}),
|
||||||
|
await orderFactoryRight.newSignedOrderAsync({
|
||||||
|
makerAddress: makerAddressRight,
|
||||||
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
||||||
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 0),
|
||||||
|
feeRecipientAddress: feeRecipientAddressRight,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
const expectedTransferAmounts = [
|
||||||
|
{
|
||||||
|
// Left Maker
|
||||||
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 0),
|
||||||
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(50, 16), // 50%
|
||||||
|
// Right Maker
|
||||||
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
||||||
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
||||||
|
// Taker
|
||||||
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(50, 16), // 50%
|
||||||
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Left Maker
|
||||||
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 0),
|
||||||
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(50, 16), // 50%
|
||||||
|
// Right Maker
|
||||||
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
||||||
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
||||||
|
// Taker
|
||||||
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(50, 16), // 50%
|
||||||
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
||||||
|
},
|
||||||
|
];
|
||||||
|
await matchOrderTester.batchMatchOrdersAndAssertEffectsAsync(
|
||||||
|
{
|
||||||
|
leftOrders,
|
||||||
|
rightOrders,
|
||||||
|
leftOrdersTakerAssetFilledAmounts: [ZERO],
|
||||||
|
rightOrdersTakerAssetFilledAmounts: [ZERO, ZERO],
|
||||||
|
},
|
||||||
|
takerAddress,
|
||||||
|
[[0, 0], [0, 1]],
|
||||||
|
expectedTransferAmounts,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
// tslint:disable-line:max-file-line-count
|
// tslint:disable-line:max-file-line-count
|
||||||
|
@ -266,6 +266,22 @@ export class ExchangeWrapper {
|
|||||||
const ordersInfo = (await this._exchange.getOrdersInfo.callAsync(signedOrders)) as OrderInfo[];
|
const ordersInfo = (await this._exchange.getOrdersInfo.callAsync(signedOrders)) as OrderInfo[];
|
||||||
return ordersInfo;
|
return ordersInfo;
|
||||||
}
|
}
|
||||||
|
public async batchMatchOrdersAsync(
|
||||||
|
signedOrdersLeft: SignedOrder[],
|
||||||
|
signedOrdersRight: SignedOrder[],
|
||||||
|
from: string,
|
||||||
|
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||||
|
const params = orderUtils.createBatchMatchOrders(signedOrdersLeft, signedOrdersRight);
|
||||||
|
const txHash = await this._exchange.batchMatchOrders.sendTransactionAsync(
|
||||||
|
params.leftOrders,
|
||||||
|
params.rightOrders,
|
||||||
|
params.leftSignatures,
|
||||||
|
params.rightSignatures,
|
||||||
|
{ from },
|
||||||
|
);
|
||||||
|
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
|
||||||
|
return tx;
|
||||||
|
}
|
||||||
public async matchOrdersAsync(
|
public async matchOrdersAsync(
|
||||||
signedOrderLeft: SignedOrder,
|
signedOrderLeft: SignedOrder,
|
||||||
signedOrderRight: SignedOrder,
|
signedOrderRight: SignedOrder,
|
||||||
|
@ -56,6 +56,11 @@ export interface MatchResults {
|
|||||||
balances: TokenBalances;
|
balances: TokenBalances;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BatchMatchResults {
|
||||||
|
matches: MatchResults[];
|
||||||
|
filledAmounts: Array<[SignedOrder, BigNumber, string]>;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ERC1155Holdings {
|
export interface ERC1155Holdings {
|
||||||
[owner: string]: {
|
[owner: string]: {
|
||||||
[contract: string]: {
|
[contract: string]: {
|
||||||
@ -81,6 +86,13 @@ export interface TokenBalances {
|
|||||||
erc1155: ERC1155Holdings;
|
erc1155: ERC1155Holdings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BatchMatchedOrders {
|
||||||
|
leftOrders: SignedOrder[];
|
||||||
|
rightOrders: SignedOrder[];
|
||||||
|
leftOrdersTakerAssetFilledAmounts: BigNumber[];
|
||||||
|
rightOrdersTakerAssetFilledAmounts: BigNumber[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface MatchedOrders {
|
export interface MatchedOrders {
|
||||||
leftOrder: SignedOrder;
|
leftOrder: SignedOrder;
|
||||||
rightOrder: SignedOrder;
|
rightOrder: SignedOrder;
|
||||||
@ -88,6 +100,12 @@ export interface MatchedOrders {
|
|||||||
rightOrderTakerAssetFilledAmount?: BigNumber;
|
rightOrderTakerAssetFilledAmount?: BigNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type BatchMatchOrdersAsyncCall = (
|
||||||
|
leftOrders: SignedOrder[],
|
||||||
|
rightOrders: SignedOrder[],
|
||||||
|
takerAddress: string,
|
||||||
|
) => Promise<TransactionReceiptWithDecodedLogs>;
|
||||||
|
|
||||||
export type MatchOrdersAsyncCall = (
|
export type MatchOrdersAsyncCall = (
|
||||||
leftOrder: SignedOrder,
|
leftOrder: SignedOrder,
|
||||||
rightOrder: SignedOrder,
|
rightOrder: SignedOrder,
|
||||||
@ -99,6 +117,7 @@ export class MatchOrderTester {
|
|||||||
public erc20Wrapper: ERC20Wrapper;
|
public erc20Wrapper: ERC20Wrapper;
|
||||||
public erc721Wrapper: ERC721Wrapper;
|
public erc721Wrapper: ERC721Wrapper;
|
||||||
public erc1155ProxyWrapper: ERC1155ProxyWrapper;
|
public erc1155ProxyWrapper: ERC1155ProxyWrapper;
|
||||||
|
public batchMatchOrdersCallAsync?: BatchMatchOrdersAsyncCall;
|
||||||
public matchOrdersCallAsync?: MatchOrdersAsyncCall;
|
public matchOrdersCallAsync?: MatchOrdersAsyncCall;
|
||||||
private readonly _initialTokenBalancesPromise: Promise<TokenBalances>;
|
private readonly _initialTokenBalancesPromise: Promise<TokenBalances>;
|
||||||
|
|
||||||
@ -108,6 +127,8 @@ export class MatchOrderTester {
|
|||||||
* @param erc20Wrapper Used to fetch ERC20 balances.
|
* @param erc20Wrapper Used to fetch ERC20 balances.
|
||||||
* @param erc721Wrapper Used to fetch ERC721 token owners.
|
* @param erc721Wrapper Used to fetch ERC721 token owners.
|
||||||
* @param erc1155Wrapper Used to fetch ERC1155 token owners.
|
* @param erc1155Wrapper Used to fetch ERC1155 token owners.
|
||||||
|
* @param batchMatchOrdersCallAsync Optional, custom caller for
|
||||||
|
* `ExchangeWrapper.batchMatchOrdersAsync()`.
|
||||||
* @param matchOrdersCallAsync Optional, custom caller for
|
* @param matchOrdersCallAsync Optional, custom caller for
|
||||||
* `ExchangeWrapper.matchOrdersAsync()`.
|
* `ExchangeWrapper.matchOrdersAsync()`.
|
||||||
*/
|
*/
|
||||||
@ -117,6 +138,7 @@ export class MatchOrderTester {
|
|||||||
erc721Wrapper: ERC721Wrapper,
|
erc721Wrapper: ERC721Wrapper,
|
||||||
erc1155ProxyWrapper: ERC1155ProxyWrapper,
|
erc1155ProxyWrapper: ERC1155ProxyWrapper,
|
||||||
matchOrdersCallAsync?: MatchOrdersAsyncCall,
|
matchOrdersCallAsync?: MatchOrdersAsyncCall,
|
||||||
|
batchMatchOrdersCallAsync?: BatchMatchOrdersAsyncCall,
|
||||||
) {
|
) {
|
||||||
this.exchangeWrapper = exchangeWrapper;
|
this.exchangeWrapper = exchangeWrapper;
|
||||||
this.erc20Wrapper = erc20Wrapper;
|
this.erc20Wrapper = erc20Wrapper;
|
||||||
@ -126,6 +148,59 @@ export class MatchOrderTester {
|
|||||||
this._initialTokenBalancesPromise = this.getBalancesAsync();
|
this._initialTokenBalancesPromise = this.getBalancesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs batch order matching on a set of complementary orders and asserts results.
|
||||||
|
* @param orders The list of orders and filled states
|
||||||
|
* @param matchPairs An array of left and right indices that will be used to perform
|
||||||
|
* the expected simulation.
|
||||||
|
* @param takerAddress Address of taker (the address who matched the two orders)
|
||||||
|
* @param expectedTransferAmounts Expected amounts transferred as a result of each round of
|
||||||
|
* order matching. Omitted fields are either set to 0 or their
|
||||||
|
* complementary field.
|
||||||
|
* @return Results of `batchMatchOrders()`.
|
||||||
|
*/
|
||||||
|
public async batchMatchOrdersAndAssertEffectsAsync(
|
||||||
|
orders: BatchMatchedOrders,
|
||||||
|
takerAddress: string,
|
||||||
|
matchPairs: Array<[number, number]>,
|
||||||
|
expectedTransferAmounts: Array<Partial<MatchTransferAmounts>>,
|
||||||
|
initialTokenBalances?: TokenBalances,
|
||||||
|
): Promise<BatchMatchResults> {
|
||||||
|
// Ensure that the provided input is valid.
|
||||||
|
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);
|
||||||
|
// Get the token balances before executing `batchMatchOrders()`.
|
||||||
|
const _initialTokenBalances = initialTokenBalances
|
||||||
|
? initialTokenBalances
|
||||||
|
: await this._initialTokenBalancesPromise;
|
||||||
|
// Execute `batchMatchOrders()`
|
||||||
|
const transactionReceipt = await this._executeBatchMatchOrdersAsync(
|
||||||
|
orders.leftOrders,
|
||||||
|
orders.rightOrders,
|
||||||
|
takerAddress,
|
||||||
|
);
|
||||||
|
// Simulate the batch order match.
|
||||||
|
const batchMatchResults = simulateBatchMatchOrders(
|
||||||
|
orders,
|
||||||
|
takerAddress,
|
||||||
|
_initialTokenBalances,
|
||||||
|
matchPairs,
|
||||||
|
expectedTransferAmounts,
|
||||||
|
);
|
||||||
|
// Validate the simulation against reality.
|
||||||
|
await assertBatchMatchResultsAsync(
|
||||||
|
batchMatchResults,
|
||||||
|
transactionReceipt,
|
||||||
|
await this.getBalancesAsync(),
|
||||||
|
_initialTokenBalances,
|
||||||
|
this.exchangeWrapper,
|
||||||
|
);
|
||||||
|
return batchMatchResults;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Matches two complementary orders and asserts results.
|
* Matches two complementary orders and asserts results.
|
||||||
* @param orders The matched orders and filled states.
|
* @param orders The matched orders and filled states.
|
||||||
@ -159,7 +234,7 @@ export class MatchOrderTester {
|
|||||||
_initialTokenBalances,
|
_initialTokenBalances,
|
||||||
toFullMatchTransferAmounts(expectedTransferAmounts),
|
toFullMatchTransferAmounts(expectedTransferAmounts),
|
||||||
);
|
);
|
||||||
// Validate the simulation against realit.
|
// Validate the simulation against reality.
|
||||||
await assertMatchResultsAsync(
|
await assertMatchResultsAsync(
|
||||||
matchResults,
|
matchResults,
|
||||||
transactionReceipt,
|
transactionReceipt,
|
||||||
@ -176,6 +251,18 @@ export class MatchOrderTester {
|
|||||||
return getTokenBalancesAsync(this.erc20Wrapper, this.erc721Wrapper, this.erc1155ProxyWrapper);
|
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 _executeMatchOrdersAsync(
|
private async _executeMatchOrdersAsync(
|
||||||
leftOrder: SignedOrder,
|
leftOrder: SignedOrder,
|
||||||
rightOrder: SignedOrder,
|
rightOrder: SignedOrder,
|
||||||
@ -227,6 +314,97 @@ function toFullMatchTransferAmounts(partial: Partial<MatchTransferAmounts>): Mat
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulates matching a batch of orders by transferring amounts defined in
|
||||||
|
* `transferAmounts` and returns the results.
|
||||||
|
* @param orders The orders being batch 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.
|
||||||
|
*/
|
||||||
|
function simulateBatchMatchOrders(
|
||||||
|
orders: BatchMatchedOrders,
|
||||||
|
takerAddress: string,
|
||||||
|
tokenBalances: TokenBalances,
|
||||||
|
matchPairs: Array<[number, number]>,
|
||||||
|
transferAmounts: Array<Partial<MatchTransferAmounts>>,
|
||||||
|
): BatchMatchResults {
|
||||||
|
// Initialize variables
|
||||||
|
let lastLeftIdx = 0;
|
||||||
|
let lastRightIdx = 0;
|
||||||
|
let matchedOrders: MatchedOrders;
|
||||||
|
const batchMatchResults: BatchMatchResults = {
|
||||||
|
matches: [],
|
||||||
|
filledAmounts: [],
|
||||||
|
};
|
||||||
|
// Loop over all of the matched pairs from the round
|
||||||
|
for (let i = 0; i < matchPairs.length; i++) {
|
||||||
|
const leftIdx = matchPairs[i][0];
|
||||||
|
const rightIdx = matchPairs[i][1];
|
||||||
|
// Construct a matched order out of the current left and right orders
|
||||||
|
matchedOrders = {
|
||||||
|
leftOrder: orders.leftOrders[leftIdx],
|
||||||
|
rightOrder: orders.rightOrders[rightIdx],
|
||||||
|
leftOrderTakerAssetFilledAmount: orders.leftOrdersTakerAssetFilledAmounts[leftIdx],
|
||||||
|
rightOrderTakerAssetFilledAmount: orders.rightOrdersTakerAssetFilledAmounts[rightIdx],
|
||||||
|
};
|
||||||
|
// If there has been a match recorded and one or both of the side indices have not changed,
|
||||||
|
// replace the side's taker asset filled amount
|
||||||
|
if (batchMatchResults.matches.length > 0) {
|
||||||
|
if (lastLeftIdx === leftIdx) {
|
||||||
|
matchedOrders.leftOrderTakerAssetFilledAmount = getLastMatch(
|
||||||
|
batchMatchResults,
|
||||||
|
).orders.leftOrderTakerAssetFilledAmount;
|
||||||
|
} else {
|
||||||
|
batchMatchResults.filledAmounts.push([
|
||||||
|
orders.leftOrders[lastLeftIdx],
|
||||||
|
getLastMatch(batchMatchResults).orders.leftOrderTakerAssetFilledAmount || ZERO,
|
||||||
|
'left',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if (lastRightIdx === rightIdx) {
|
||||||
|
matchedOrders.rightOrderTakerAssetFilledAmount = getLastMatch(
|
||||||
|
batchMatchResults,
|
||||||
|
).orders.rightOrderTakerAssetFilledAmount;
|
||||||
|
} else {
|
||||||
|
batchMatchResults.filledAmounts.push([
|
||||||
|
orders.rightOrders[lastRightIdx],
|
||||||
|
getLastMatch(batchMatchResults).orders.rightOrderTakerAssetFilledAmount || ZERO,
|
||||||
|
'right',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastLeftIdx = leftIdx;
|
||||||
|
lastRightIdx = rightIdx;
|
||||||
|
// Add the latest match to the batch match results
|
||||||
|
batchMatchResults.matches.push(
|
||||||
|
simulateMatchOrders(
|
||||||
|
matchedOrders,
|
||||||
|
takerAddress,
|
||||||
|
tokenBalances,
|
||||||
|
toFullMatchTransferAmounts(transferAmounts[i]),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// The two orders indexed by lastLeftIdx and lastRightIdx were potentially
|
||||||
|
// filled; however, the TakerAssetFilledAmounts that pertain to these orders
|
||||||
|
// will not have been added to batchMatchResults, so we need to write them
|
||||||
|
// here.
|
||||||
|
batchMatchResults.filledAmounts.push([
|
||||||
|
orders.leftOrders[lastLeftIdx],
|
||||||
|
getLastMatch(batchMatchResults).orders.leftOrderTakerAssetFilledAmount || ZERO,
|
||||||
|
'left',
|
||||||
|
]);
|
||||||
|
batchMatchResults.filledAmounts.push([
|
||||||
|
orders.rightOrders[lastRightIdx],
|
||||||
|
getLastMatch(batchMatchResults).orders.rightOrderTakerAssetFilledAmount || ZERO,
|
||||||
|
'right',
|
||||||
|
]);
|
||||||
|
// Return the batch match results
|
||||||
|
return batchMatchResults;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simulates matching two orders by transferring amounts defined in
|
* Simulates matching two orders by transferring amounts defined in
|
||||||
* `transferAmounts` and returns the results.
|
* `transferAmounts` and returns the results.
|
||||||
@ -406,6 +584,34 @@ function transferAsset(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that the results of `simulateBatchMatchOrders()` agrees with reality.
|
||||||
|
* @param batchMatchResults The results of a `simulateBatchMatchOrders()`.
|
||||||
|
* @param transactionReceipt The transaction receipt of a call to `matchOrders()`.
|
||||||
|
* @param actualTokenBalances The actual, on-chain token balances of known addresses.
|
||||||
|
* @param exchangeWrapper The ExchangeWrapper instance.
|
||||||
|
*/
|
||||||
|
async function assertBatchMatchResultsAsync(
|
||||||
|
batchMatchResults: BatchMatchResults,
|
||||||
|
transactionReceipt: TransactionReceiptWithDecodedLogs,
|
||||||
|
actualTokenBalances: TokenBalances,
|
||||||
|
initialTokenBalances: TokenBalances,
|
||||||
|
exchangeWrapper: ExchangeWrapper,
|
||||||
|
): 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);
|
||||||
|
// Check the Exchange state.
|
||||||
|
await assertPostBatchExchangeStateAsync(batchMatchResults, exchangeWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks that the results of `simulateMatchOrders()` agrees with reality.
|
* Checks that the results of `simulateMatchOrders()` agrees with reality.
|
||||||
* @param matchResults The results of a `simulateMatchOrders()`.
|
* @param matchResults The results of a `simulateMatchOrders()`.
|
||||||
@ -528,9 +734,36 @@ function assertBalances(expectedBalances: TokenBalances, actualBalances: TokenBa
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asserts initial exchange state for matched orders.
|
* Asserts the initial exchange state for batch matched orders.
|
||||||
|
* @param orders Batch matched orders with intial filled amounts.
|
||||||
|
* @param exchangeWrapper ExchangeWrapper instance.
|
||||||
|
*/
|
||||||
|
async function assertBatchOrderStatesAsync(
|
||||||
|
orders: BatchMatchedOrders,
|
||||||
|
exchangeWrapper: ExchangeWrapper,
|
||||||
|
): Promise<void> {
|
||||||
|
for (let i = 0; i < orders.leftOrders.length; i++) {
|
||||||
|
await assertOrderFilledAmountAsync(
|
||||||
|
orders.leftOrders[i],
|
||||||
|
orders.leftOrdersTakerAssetFilledAmounts[i],
|
||||||
|
'left',
|
||||||
|
exchangeWrapper,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (let i = 0; i < orders.rightOrders.length; i++) {
|
||||||
|
await assertOrderFilledAmountAsync(
|
||||||
|
orders.rightOrders[i],
|
||||||
|
orders.rightOrdersTakerAssetFilledAmounts[i],
|
||||||
|
'right',
|
||||||
|
exchangeWrapper,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the initial exchange state for matched orders.
|
||||||
* @param orders Matched orders with intial filled amounts.
|
* @param orders Matched orders with intial filled amounts.
|
||||||
* @param exchangeWrapper ExchangeWrapper isntance.
|
* @param exchangeWrapper ExchangeWrapper instance.
|
||||||
*/
|
*/
|
||||||
async function assertInitialOrderStatesAsync(orders: MatchedOrders, exchangeWrapper: ExchangeWrapper): Promise<void> {
|
async function assertInitialOrderStatesAsync(orders: MatchedOrders, exchangeWrapper: ExchangeWrapper): Promise<void> {
|
||||||
const pairs = [
|
const pairs = [
|
||||||
@ -540,13 +773,23 @@ async function assertInitialOrderStatesAsync(orders: MatchedOrders, exchangeWrap
|
|||||||
await Promise.all(
|
await Promise.all(
|
||||||
pairs.map(async ([order, expectedFilledAmount]) => {
|
pairs.map(async ([order, expectedFilledAmount]) => {
|
||||||
const side = order === orders.leftOrder ? 'left' : 'right';
|
const side = order === orders.leftOrder ? 'left' : 'right';
|
||||||
const orderHash = orderHashUtils.getOrderHashHex(order);
|
await assertOrderFilledAmountAsync(order, expectedFilledAmount, side, exchangeWrapper);
|
||||||
const actualFilledAmount = await exchangeWrapper.getTakerAssetFilledAmountAsync(orderHash);
|
|
||||||
expect(actualFilledAmount, `${side} order initial filled amount`).to.bignumber.equal(expectedFilledAmount);
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the exchange state after a call to `batchMatchOrders()`.
|
||||||
|
* @param batchMatchResults Results from a call to `simulateBatchMatchOrders()`.
|
||||||
|
* @param exchangeWrapper The ExchangeWrapper instance.
|
||||||
|
*/
|
||||||
|
async function assertPostBatchExchangeStateAsync(
|
||||||
|
batchMatchResults: BatchMatchResults,
|
||||||
|
exchangeWrapper: ExchangeWrapper,
|
||||||
|
): Promise<void> {
|
||||||
|
await assertTriplesExchangeStateAsync(batchMatchResults.filledAmounts, exchangeWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asserts the exchange state after a call to `matchOrders()`.
|
* Asserts the exchange state after a call to `matchOrders()`.
|
||||||
* @param matchResults Results from a call to `simulateMatchOrders()`.
|
* @param matchResults Results from a call to `simulateMatchOrders()`.
|
||||||
@ -556,13 +799,48 @@ async function assertPostExchangeStateAsync(
|
|||||||
matchResults: MatchResults,
|
matchResults: MatchResults,
|
||||||
exchangeWrapper: ExchangeWrapper,
|
exchangeWrapper: ExchangeWrapper,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const pairs = [
|
const triples = [
|
||||||
[matchResults.orders.leftOrder, matchResults.orders.leftOrderTakerAssetFilledAmount],
|
[matchResults.orders.leftOrder, matchResults.orders.leftOrderTakerAssetFilledAmount, 'left'],
|
||||||
[matchResults.orders.rightOrder, matchResults.orders.rightOrderTakerAssetFilledAmount],
|
[matchResults.orders.rightOrder, matchResults.orders.rightOrderTakerAssetFilledAmount, 'right'],
|
||||||
] as Array<[SignedOrder, BigNumber]>;
|
] as Array<[SignedOrder, BigNumber, string]>;
|
||||||
|
await assertTriplesExchangeStateAsync(triples, exchangeWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the exchange state represented by provided sequence of triples.
|
||||||
|
* @param triples The sequence of triples to verifiy. Each triple consists
|
||||||
|
* of an `order`, a `takerAssetFilledAmount`, and a `side`,
|
||||||
|
* which will be used to determine if the exchange's state
|
||||||
|
* is valid.
|
||||||
|
* @param exchangeWrapper The ExchangeWrapper instance.
|
||||||
|
*/
|
||||||
|
async function assertTriplesExchangeStateAsync(
|
||||||
|
triples: Array<[SignedOrder, BigNumber, string]>,
|
||||||
|
exchangeWrapper: ExchangeWrapper,
|
||||||
|
): Promise<void> {
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
pairs.map(async ([order, expectedFilledAmount]) => {
|
triples.map(async ([order, expectedFilledAmount, side]) => {
|
||||||
const side = order === matchResults.orders.leftOrder ? 'left' : 'right';
|
expect(['left', 'right']).to.include(side);
|
||||||
|
await assertOrderFilledAmountAsync(order, expectedFilledAmount, side, exchangeWrapper);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
async function assertOrderFilledAmountAsync(
|
||||||
|
order: SignedOrder,
|
||||||
|
expectedFilledAmount: BigNumber,
|
||||||
|
side: string,
|
||||||
|
exchangeWrapper: ExchangeWrapper,
|
||||||
|
): Promise<void> {
|
||||||
const orderInfo = await exchangeWrapper.getOrderInfoAsync(order);
|
const orderInfo = await exchangeWrapper.getOrderInfoAsync(order);
|
||||||
// Check filled amount of order.
|
// Check filled amount of order.
|
||||||
const actualFilledAmount = orderInfo.orderTakerAssetFilledAmount;
|
const actualFilledAmount = orderInfo.orderTakerAssetFilledAmount;
|
||||||
@ -573,12 +851,10 @@ async function assertPostExchangeStateAsync(
|
|||||||
: OrderStatus.Fillable;
|
: OrderStatus.Fillable;
|
||||||
const actualStatus = orderInfo.orderStatus;
|
const actualStatus = orderInfo.orderStatus;
|
||||||
expect(actualStatus, `${side} order final status`).to.equal(expectedStatus);
|
expect(actualStatus, `${side} order final status`).to.equal(expectedStatus);
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrive the current token balances of all known addresses.
|
* Retrieve the current token balances of all known addresses.
|
||||||
* @param erc20Wrapper The ERC20Wrapper instance.
|
* @param erc20Wrapper The ERC20Wrapper instance.
|
||||||
* @param erc721Wrapper The ERC721Wrapper instance.
|
* @param erc721Wrapper The ERC721Wrapper instance.
|
||||||
* @param erc1155Wrapper The ERC1155ProxyWrapper instance.
|
* @param erc1155Wrapper The ERC1155ProxyWrapper instance.
|
||||||
@ -634,4 +910,61 @@ function encodeTokenBalances(obj: any): any {
|
|||||||
const keys = _.keys(obj).sort();
|
const keys = _.keys(obj).sort();
|
||||||
return _.zip(keys, keys.map(k => encodeTokenBalances(obj[k])));
|
return _.zip(keys, keys.map(k => encodeTokenBalances(obj[k])));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the last match in a BatchMatchResults object.
|
||||||
|
* @param batchMatchResults The BatchMatchResults object.
|
||||||
|
* @return The last match of the results.
|
||||||
|
*/
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes a `totalBalances`, a `balances`, and an `initialBalances`, subtracts the `initialBalances
|
||||||
|
* from the `balances`, and then adds the result to `totalBalances`.
|
||||||
|
* @param totalBalances A set of balances to be updated with new results.
|
||||||
|
* @param balances A new set of results that deviate from the `initialBalances` by one matched
|
||||||
|
* order. Subtracting away the `initialBalances` leaves behind a diff of the
|
||||||
|
* matched orders effect on the `initialBalances`.
|
||||||
|
* @param initialBalances The token balances from before the call to `batchMatchOrders()`.
|
||||||
|
* @return The updated total balances using the derived balance difference.
|
||||||
|
*/
|
||||||
|
function aggregateBalances(
|
||||||
|
totalBalances: TokenBalances,
|
||||||
|
balances: TokenBalances,
|
||||||
|
initialBalances: TokenBalances,
|
||||||
|
): TokenBalances {
|
||||||
|
// ERC20
|
||||||
|
for (const owner of _.keys(totalBalances.erc20)) {
|
||||||
|
for (const contract of _.keys(totalBalances.erc20[owner])) {
|
||||||
|
const difference = balances.erc20[owner][contract].minus(initialBalances.erc20[owner][contract]);
|
||||||
|
totalBalances.erc20[owner][contract] = totalBalances.erc20[owner][contract].plus(difference);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ERC721
|
||||||
|
for (const owner of _.keys(totalBalances.erc721)) {
|
||||||
|
for (const contract of _.keys(totalBalances.erc721[owner])) {
|
||||||
|
totalBalances.erc721[owner][contract] = _.zipWith(
|
||||||
|
totalBalances.erc721[owner][contract],
|
||||||
|
balances.erc721[owner][contract],
|
||||||
|
initialBalances.erc721[owner][contract],
|
||||||
|
(a: BigNumber, b: BigNumber, c: BigNumber) => a.plus(b.minus(c)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO(jalextowle): Implement the same as the above for ERC1155
|
||||||
|
return totalBalances;
|
||||||
|
}
|
||||||
// tslint:disable-line:max-file-line-count
|
// tslint:disable-line:max-file-line-count
|
||||||
|
@ -5,7 +5,7 @@ import { BigNumber } from '@0x/utils';
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { constants } from './constants';
|
import { constants } from './constants';
|
||||||
import { CancelOrder, MatchOrder } from './types';
|
import { BatchMatchOrder, CancelOrder, MatchOrder } from './types';
|
||||||
|
|
||||||
export const orderUtils = {
|
export const orderUtils = {
|
||||||
getPartialAmountFloor(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber {
|
getPartialAmountFloor(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber {
|
||||||
@ -33,6 +33,19 @@ export const orderUtils = {
|
|||||||
getOrderWithoutDomain(signedOrder: SignedOrder): OrderWithoutDomain {
|
getOrderWithoutDomain(signedOrder: SignedOrder): OrderWithoutDomain {
|
||||||
return _.omit(signedOrder, ['signature', 'domain']) as OrderWithoutDomain;
|
return _.omit(signedOrder, ['signature', 'domain']) as OrderWithoutDomain;
|
||||||
},
|
},
|
||||||
|
createBatchMatchOrders(signedOrdersLeft: SignedOrder[], signedOrdersRight: SignedOrder[]): BatchMatchOrder {
|
||||||
|
return {
|
||||||
|
leftOrders: signedOrdersLeft.map(order => orderUtils.getOrderWithoutDomain(order)),
|
||||||
|
rightOrders: signedOrdersRight.map(order => {
|
||||||
|
const right = orderUtils.getOrderWithoutDomain(order);
|
||||||
|
right.makerAssetData = constants.NULL_BYTES;
|
||||||
|
right.takerAssetData = constants.NULL_BYTES;
|
||||||
|
return right;
|
||||||
|
}),
|
||||||
|
leftSignatures: signedOrdersLeft.map(order => order.signature),
|
||||||
|
rightSignatures: signedOrdersRight.map(order => order.signature),
|
||||||
|
};
|
||||||
|
},
|
||||||
createMatchOrders(signedOrderLeft: SignedOrder, signedOrderRight: SignedOrder): MatchOrder {
|
createMatchOrders(signedOrderLeft: SignedOrder, signedOrderRight: SignedOrder): MatchOrder {
|
||||||
const fill = {
|
const fill = {
|
||||||
left: orderUtils.getOrderWithoutDomain(signedOrderLeft),
|
left: orderUtils.getOrderWithoutDomain(signedOrderLeft),
|
||||||
|
@ -130,6 +130,13 @@ export interface CancelOrder {
|
|||||||
takerAssetCancelAmount: BigNumber;
|
takerAssetCancelAmount: BigNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BatchMatchOrder {
|
||||||
|
leftOrders: OrderWithoutDomain[];
|
||||||
|
rightOrders: OrderWithoutDomain[];
|
||||||
|
leftSignatures: string[];
|
||||||
|
rightSignatures: string[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface MatchOrder {
|
export interface MatchOrder {
|
||||||
left: OrderWithoutDomain;
|
left: OrderWithoutDomain;
|
||||||
right: OrderWithoutDomain;
|
right: OrderWithoutDomain;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user