protocol/contracts/integrations/test/exchange/batch_match_orders_test.ts

861 lines
40 KiB
TypeScript

import { encodeERC1155AssetData, encodeERC20AssetData, encodeERC721AssetData } from '@0x/contracts-asset-proxy';
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
import { ExchangeRevertErrors } from '@0x/contracts-exchange';
import { blockchainTests, constants, expect, toBaseUnitAmount } from '@0x/contracts-test-utils';
import { Order, SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { Actor } from '../framework/actors/base';
import { Maker } from '../framework/actors/maker';
import { actorAddressesByName } from '../framework/actors/utils';
import { BlockchainBalanceStore } from '../framework/balances/blockchain_balance_store';
import { TokenIds } from '../framework/balances/types';
import { DeploymentManager } from '../framework/deployment_manager';
import { MatchOrderTester, MatchTransferAmounts } from './match_order_tester';
blockchainTests.resets('matchOrders integration tests', env => {
// The fee recipient addresses.
let feeRecipientLeft: Actor;
let feeRecipientRight: Actor;
// The address that should be responsible for matching orders.
let matcher: Actor;
// Market makers who have opposite maker and taker assets.
let makerLeft: Maker;
let makerRight: Maker;
// The addresses of important assets for testing.
let makerAssetLeft: DummyERC20TokenContract;
let makerAssetRight: DummyERC20TokenContract;
let feeAsset: DummyERC20TokenContract;
let makerAssetDataLeft: string;
let makerAssetDataRight: string;
let feeAssetData: string;
let deployment: DeploymentManager;
let matchOrderTester: MatchOrderTester;
let leftId: BigNumber;
let rightId: BigNumber;
before(async () => {
deployment = await DeploymentManager.deployAsync(env, {
numErc20TokensToDeploy: 3,
numErc721TokensToDeploy: 1,
numErc1155TokensToDeploy: 1,
});
makerAssetLeft = deployment.tokens.erc20[0];
makerAssetRight = deployment.tokens.erc20[1];
feeAsset = deployment.tokens.erc20[2];
// Create the fee recipient actors.
feeRecipientLeft = new Actor({
name: 'left fee recipient',
deployment,
});
feeRecipientRight = new Actor({
name: 'right fee recipient',
deployment,
});
// Encode the asset data.
makerAssetDataLeft = encodeERC20AssetData(makerAssetLeft.address);
makerAssetDataRight = encodeERC20AssetData(makerAssetRight.address);
feeAssetData = encodeERC20AssetData(feeAsset.address);
// Create two market makers with compatible orders for matching.
makerLeft = new Maker({
name: 'left maker',
deployment,
orderConfig: {
makerAssetData: makerAssetDataLeft,
takerAssetData: makerAssetDataRight,
makerFeeAssetData: feeAssetData,
takerFeeAssetData: feeAssetData,
feeRecipientAddress: feeRecipientLeft.address,
},
});
makerRight = new Maker({
name: 'right maker',
deployment,
orderConfig: {
makerAssetData: makerAssetDataRight,
takerAssetData: makerAssetDataLeft,
makerFeeAssetData: feeAssetData,
takerFeeAssetData: feeAssetData,
feeRecipientAddress: feeRecipientRight.address,
},
});
// Create a matcher.
matcher = new Actor({
name: 'matcher',
deployment,
});
// Configure the appropriate actors with initial balances.
await Promise.all([
...deployment.tokens.erc20.map(async token => makerLeft.configureERC20TokenAsync(token)),
...deployment.tokens.erc20.map(async token => makerRight.configureERC20TokenAsync(token)),
makerLeft.configureERC20TokenAsync(deployment.tokens.weth, deployment.staking.stakingProxy.address),
makerRight.configureERC20TokenAsync(deployment.tokens.weth, deployment.staking.stakingProxy.address),
matcher.configureERC20TokenAsync(feeAsset),
matcher.configureERC20TokenAsync(deployment.tokens.weth, deployment.staking.stakingProxy.address),
feeRecipientLeft.configureERC20TokenAsync(feeAsset),
feeRecipientLeft.configureERC20TokenAsync(deployment.tokens.weth, deployment.staking.stakingProxy.address),
feeRecipientRight.configureERC20TokenAsync(feeAsset),
feeRecipientRight.configureERC20TokenAsync(deployment.tokens.weth, deployment.staking.stakingProxy.address),
]);
leftId = await makerLeft.configureERC1155TokenAsync(deployment.tokens.erc1155[0]);
[rightId] = await makerRight.configureERC721TokenAsync(deployment.tokens.erc721[0]);
const tokenIds: TokenIds = { erc721: {}, erc1155: {} };
tokenIds.erc1155[deployment.tokens.erc1155[0].address] = { fungible: [leftId], nonFungible: [] };
tokenIds.erc721[deployment.tokens.erc721[0].address] = [rightId];
const blockchainBalanceStore = new BlockchainBalanceStore(
{
...actorAddressesByName([feeRecipientLeft, feeRecipientRight, makerLeft, makerRight, matcher]),
stakingProxy: deployment.staking.stakingProxy.address,
},
{
erc20: {
makerTokenLeft: deployment.tokens.erc20[0],
makerTokenRight: deployment.tokens.erc20[1],
feeToken: deployment.tokens.erc20[2],
weth: deployment.tokens.weth,
},
erc721: {
nft: deployment.tokens.erc721[0],
},
erc1155: {
fungible: deployment.tokens.erc1155[0],
},
},
tokenIds,
);
matchOrderTester = new MatchOrderTester(deployment, blockchainBalanceStore);
});
after(async () => {
Actor.reset();
});
describe('batchMatchOrders and batchMatchOrdersWithMaximalFill rich errors', async () => {
it('should fail if there are zero leftOrders with the ZeroLeftOrders rich error reason', async () => {
const leftOrders: SignedOrder[] = [];
const rightOrders = [
await makerRight.signOrderAsync({
makerAssetAmount: new BigNumber(1),
takerAssetAmount: new BigNumber(2),
}),
];
const expectedError = new ExchangeRevertErrors.BatchMatchOrdersError(
ExchangeRevertErrors.BatchMatchOrdersErrorCodes.ZeroLeftOrders,
);
let tx = deployment.exchange
.batchMatchOrders(
leftOrders,
rightOrders,
leftOrders.map(order => order.signature),
rightOrders.map(order => order.signature),
)
.awaitTransactionSuccessAsync({ from: matcher.address });
await expect(tx).to.revertWith(expectedError);
tx = deployment.exchange
.batchMatchOrdersWithMaximalFill(
leftOrders,
rightOrders,
leftOrders.map(order => order.signature),
rightOrders.map(order => order.signature),
)
.awaitTransactionSuccessAsync({ from: matcher.address });
return expect(tx).to.revertWith(expectedError);
});
it('should fail if there are zero rightOrders', async () => {
const leftOrders = [
await makerLeft.signOrderAsync({
makerAssetAmount: new BigNumber(1),
takerAssetAmount: new BigNumber(2),
}),
];
const rightOrders: SignedOrder[] = [];
const expectedError = new ExchangeRevertErrors.BatchMatchOrdersError(
ExchangeRevertErrors.BatchMatchOrdersErrorCodes.ZeroRightOrders,
);
let tx = deployment.exchange
.batchMatchOrders(
leftOrders,
rightOrders,
leftOrders.map(order => order.signature),
rightOrders.map(order => order.signature),
)
.awaitTransactionSuccessAsync({ from: matcher.address });
await expect(tx).to.revertWith(expectedError);
tx = deployment.exchange
.batchMatchOrdersWithMaximalFill(
leftOrders,
rightOrders,
leftOrders.map(order => order.signature),
rightOrders.map(order => order.signature),
)
.awaitTransactionSuccessAsync({ from: matcher.address });
return expect(tx).to.revertWith(expectedError);
});
it('should fail if there are a different number of left orders and signatures', async () => {
const leftOrders = [
await makerLeft.signOrderAsync({
makerAssetAmount: new BigNumber(2),
takerAssetAmount: new BigNumber(1),
}),
await makerRight.signOrderAsync({
makerAssetAmount: new BigNumber(1),
takerAssetAmount: new BigNumber(2),
}),
];
const rightOrders = [
await makerRight.signOrderAsync({
makerAssetAmount: new BigNumber(1),
takerAssetAmount: new BigNumber(2),
}),
await makerRight.signOrderAsync({
makerAssetAmount: new BigNumber(1),
takerAssetAmount: new BigNumber(2),
}),
];
const expectedError = new ExchangeRevertErrors.BatchMatchOrdersError(
ExchangeRevertErrors.BatchMatchOrdersErrorCodes.InvalidLengthLeftSignatures,
);
let tx = deployment.exchange
.batchMatchOrders(
leftOrders,
rightOrders,
[leftOrders[0].signature],
rightOrders.map(order => order.signature),
)
.awaitTransactionSuccessAsync({ from: matcher.address });
await expect(tx).to.revertWith(expectedError);
tx = deployment.exchange
.batchMatchOrdersWithMaximalFill(
leftOrders,
rightOrders,
[leftOrders[0].signature],
rightOrders.map(order => order.signature),
)
.awaitTransactionSuccessAsync({ from: matcher.address });
return expect(tx).to.revertWith(expectedError);
});
it('should fail if there are a different number of right orders and signatures', async () => {
const leftOrders = [
await makerLeft.signOrderAsync({
makerAssetAmount: new BigNumber(2),
takerAssetAmount: new BigNumber(1),
}),
await makerLeft.signOrderAsync({
makerAssetAmount: new BigNumber(2),
takerAssetAmount: new BigNumber(1),
}),
];
const rightOrders = [
await makerRight.signOrderAsync({
makerAssetAmount: new BigNumber(1),
takerAssetAmount: new BigNumber(2),
}),
await makerRight.signOrderAsync({
makerAssetAmount: new BigNumber(1),
takerAssetAmount: new BigNumber(2),
}),
];
const expectedError = new ExchangeRevertErrors.BatchMatchOrdersError(
ExchangeRevertErrors.BatchMatchOrdersErrorCodes.InvalidLengthRightSignatures,
);
let tx = deployment.exchange
.batchMatchOrders(leftOrders, rightOrders, leftOrders.map(order => order.signature), [
rightOrders[0].signature,
])
.awaitTransactionSuccessAsync({ from: matcher.address });
await expect(tx).to.revertWith(expectedError);
tx = deployment.exchange
.batchMatchOrdersWithMaximalFill(leftOrders, rightOrders, leftOrders.map(order => order.signature), [
rightOrders[0].signature,
])
.awaitTransactionSuccessAsync({ from: matcher.address });
return expect(tx).to.revertWith(expectedError);
});
});
interface TestBatchMatchOrdersArgs {
leftOrders: Array<Partial<Order>>;
rightOrders: Array<Partial<Order>>;
expectedTransferAmounts: Array<Partial<MatchTransferAmounts>>;
leftOrdersTakerAssetFilledAmounts: BigNumber[];
rightOrdersTakerAssetFilledAmounts: BigNumber[];
matchIndices: Array<[number, number]>;
shouldMaximallyFill: boolean;
matcherAddress?: string;
}
/**
* Tests a batch order matching scenario with both eth and weth protocol fees.
*/
async function testBatchMatchOrdersAsync(args: TestBatchMatchOrdersArgs): Promise<void> {
const signedLeftOrders = await Promise.all(args.leftOrders.map(async order => makerLeft.signOrderAsync(order)));
const signedRightOrders = await Promise.all(
args.rightOrders.map(async order => makerRight.signOrderAsync(order)),
);
await matchOrderTester.batchMatchOrdersAndAssertEffectsAsync(
{
leftOrders: signedLeftOrders,
rightOrders: signedRightOrders,
leftOrdersTakerAssetFilledAmounts: args.leftOrdersTakerAssetFilledAmounts,
rightOrdersTakerAssetFilledAmounts: args.rightOrdersTakerAssetFilledAmounts,
},
args.matcherAddress || matcher.address,
DeploymentManager.protocolFee.times(args.matchIndices.length).times(2),
args.matchIndices,
args.expectedTransferAmounts.map(transferAmounts => {
return {
...transferAmounts,
leftProtocolFeePaidByTakerInEthAmount: DeploymentManager.protocolFee,
rightProtocolFeePaidByTakerInEthAmount: DeploymentManager.protocolFee,
leftProtocolFeePaidByTakerInWethAmount: constants.ZERO_AMOUNT,
rightProtocolFeePaidByTakerInWethAmount: constants.ZERO_AMOUNT,
};
}),
args.shouldMaximallyFill,
);
await env.blockchainLifecycle.revertAsync();
await env.blockchainLifecycle.startAsync();
await matchOrderTester.batchMatchOrdersAndAssertEffectsAsync(
{
leftOrders: signedLeftOrders,
rightOrders: signedRightOrders,
leftOrdersTakerAssetFilledAmounts: args.leftOrdersTakerAssetFilledAmounts,
rightOrdersTakerAssetFilledAmounts: args.rightOrdersTakerAssetFilledAmounts,
},
args.matcherAddress || matcher.address,
constants.ZERO_AMOUNT,
args.matchIndices,
args.expectedTransferAmounts.map(transferAmounts => {
return {
...transferAmounts,
leftProtocolFeePaidByTakerInEthAmount: constants.ZERO_AMOUNT,
rightProtocolFeePaidByTakerInEthAmount: constants.ZERO_AMOUNT,
leftProtocolFeePaidByTakerInWethAmount: DeploymentManager.protocolFee,
rightProtocolFeePaidByTakerInWethAmount: DeploymentManager.protocolFee,
};
}),
args.shouldMaximallyFill,
);
}
describe('batchMatchOrders', () => {
it('should correctly match two opposite orders', async () => {
await testBatchMatchOrdersAsync({
leftOrders: [
{
makerAssetAmount: new BigNumber(2),
takerAssetAmount: new BigNumber(1),
},
],
rightOrders: [
{
makerAssetAmount: new BigNumber(1),
takerAssetAmount: new BigNumber(2),
},
],
expectedTransferAmounts: [
{
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(2),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: new BigNumber(1),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Taker
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
},
],
leftOrdersTakerAssetFilledAmounts: [constants.ZERO_AMOUNT],
rightOrdersTakerAssetFilledAmounts: [constants.ZERO_AMOUNT],
matchIndices: [[0, 0]],
shouldMaximallyFill: false,
});
});
it('should correctly match a partial fill', async () => {
await testBatchMatchOrdersAsync({
leftOrders: [
{
makerAssetAmount: new BigNumber(4),
takerAssetAmount: new BigNumber(2),
},
],
rightOrders: [
{
makerAssetAmount: new BigNumber(1),
takerAssetAmount: new BigNumber(2),
},
],
expectedTransferAmounts: [
{
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(2),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(50, 16), // 50%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: new BigNumber(1),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Taker
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(50, 16), // 50%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
},
],
leftOrdersTakerAssetFilledAmounts: [constants.ZERO_AMOUNT],
rightOrdersTakerAssetFilledAmounts: [constants.ZERO_AMOUNT],
matchIndices: [[0, 0]],
shouldMaximallyFill: false,
});
});
it('should correctly match two left orders to one complementary right order', async () => {
await testBatchMatchOrdersAsync({
leftOrders: [
{
makerAssetAmount: new BigNumber(2),
takerAssetAmount: new BigNumber(1),
},
{
makerAssetAmount: new BigNumber(2),
takerAssetAmount: new BigNumber(1),
},
],
rightOrders: [
{
makerAssetAmount: new BigNumber(2),
takerAssetAmount: new BigNumber(4),
},
],
expectedTransferAmounts: [
{
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(2),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Right Maker
leftMakerAssetBoughtByRightMakerAmount: new BigNumber(2),
rightMakerAssetSoldByRightMakerAmount: new BigNumber(1),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(50, 16), // 50%
// Taker
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 50%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(50, 16), // 50%
},
{
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(2),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 50%
// Right Maker
leftMakerAssetBoughtByRightMakerAmount: new BigNumber(2),
rightMakerAssetSoldByRightMakerAmount: new BigNumber(1),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(50, 16), // 50%
// Taker
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 50%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(50, 16), // 50%
},
],
leftOrdersTakerAssetFilledAmounts: [constants.ZERO_AMOUNT, constants.ZERO_AMOUNT],
rightOrdersTakerAssetFilledAmounts: [constants.ZERO_AMOUNT],
matchIndices: [[0, 0], [1, 0]],
shouldMaximallyFill: false,
});
});
it('should correctly match one left order to two complementary right orders', async () => {
await testBatchMatchOrdersAsync({
leftOrders: [
{
makerAssetAmount: new BigNumber(4),
takerAssetAmount: new BigNumber(2),
},
],
rightOrders: [
{
makerAssetAmount: new BigNumber(1),
takerAssetAmount: new BigNumber(2),
},
{
makerAssetAmount: new BigNumber(1),
takerAssetAmount: new BigNumber(2),
},
],
expectedTransferAmounts: [
{
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(2),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(50, 16), // 50%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: new BigNumber(1),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Taker
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(50, 16), // 50%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
},
{
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(2),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(50, 16), // 50%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: new BigNumber(1),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Taker
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(50, 16), // 50%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
},
],
leftOrdersTakerAssetFilledAmounts: [constants.ZERO_AMOUNT],
rightOrdersTakerAssetFilledAmounts: [constants.ZERO_AMOUNT, constants.ZERO_AMOUNT],
matchIndices: [[0, 0], [0, 1]],
shouldMaximallyFill: false,
});
});
it('should correctly match one left order to two right orders, where the last should not be touched', async () => {
await testBatchMatchOrdersAsync({
leftOrders: [
{
makerAssetAmount: new BigNumber(2),
takerAssetAmount: new BigNumber(1),
},
],
rightOrders: [
{
makerAssetAmount: new BigNumber(1),
takerAssetAmount: new BigNumber(2),
},
{
makerAssetAmount: new BigNumber(1),
takerAssetAmount: new BigNumber(2),
},
],
expectedTransferAmounts: [
{
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(2),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: new BigNumber(1),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Taker
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
},
],
leftOrdersTakerAssetFilledAmounts: [constants.ZERO_AMOUNT],
rightOrdersTakerAssetFilledAmounts: [constants.ZERO_AMOUNT, constants.ZERO_AMOUNT],
matchIndices: [[0, 0]],
shouldMaximallyFill: false,
});
});
it('should have three order matchings with only two left orders and two right orders', async () => {
await testBatchMatchOrdersAsync({
leftOrders: [
{
makerAssetAmount: new BigNumber(4),
takerAssetAmount: new BigNumber(2),
},
{
makerAssetAmount: new BigNumber(2),
takerAssetAmount: new BigNumber(1),
},
],
rightOrders: [
{
makerAssetAmount: new BigNumber(1),
takerAssetAmount: new BigNumber(2),
},
{
makerAssetAmount: new BigNumber(2),
takerAssetAmount: new BigNumber(4),
},
],
expectedTransferAmounts: [
{
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(2),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(50, 16), // 50%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: new BigNumber(1),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Taker
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(50, 16), // 50%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
},
{
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(2),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(50, 16), // 50%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: new BigNumber(1),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(50, 16), // 50%
// Taker
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(50, 16), // 50%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(50, 16), // 50%
},
{
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(2),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: new BigNumber(1),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(50, 16), // 50%
// Taker
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(50, 16), // 50%
},
],
leftOrdersTakerAssetFilledAmounts: [constants.ZERO_AMOUNT, constants.ZERO_AMOUNT],
rightOrdersTakerAssetFilledAmounts: [constants.ZERO_AMOUNT, constants.ZERO_AMOUNT],
matchIndices: [[0, 0], [0, 1], [1, 1]],
shouldMaximallyFill: false,
});
});
});
describe('batchMatchOrdersWithMaximalFill', () => {
it('should fully fill the the right order and pay the profit denominated in the left maker asset', async () => {
await testBatchMatchOrdersAsync({
leftOrders: [
{
makerAssetAmount: new BigNumber(17),
takerAssetAmount: new BigNumber(98),
},
],
rightOrders: [
{
makerAssetAmount: new BigNumber(75),
takerAssetAmount: new BigNumber(13),
},
],
expectedTransferAmounts: [
{
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(13),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount('76.4705882352941176', 16), // 76.47%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: new BigNumber(75),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Taker
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount('76.5306122448979591', 16), // 76.53%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
},
],
leftOrdersTakerAssetFilledAmounts: [constants.ZERO_AMOUNT],
rightOrdersTakerAssetFilledAmounts: [constants.ZERO_AMOUNT],
matchIndices: [[0, 0]],
shouldMaximallyFill: true,
});
});
it('should transfer correct amounts when left order is fully filled', async () => {
await testBatchMatchOrdersAsync({
leftOrders: [
{
makerAssetAmount: new BigNumber(15),
takerAssetAmount: new BigNumber(90),
},
],
rightOrders: [
{
makerAssetAmount: new BigNumber(196),
takerAssetAmount: new BigNumber(28),
},
],
expectedTransferAmounts: [
{
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(15),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
rightMakerAssetBoughtByLeftMakerAmount: new BigNumber(90),
// Right Maker
leftMakerAssetBoughtByRightMakerAmount: new BigNumber(15),
rightMakerAssetSoldByRightMakerAmount: new BigNumber(105),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount('53.5714285714285714', 16), // 53.57%
// Taker
rightMakerAssetReceivedByTakerAmount: new BigNumber(15),
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount('53.5714285714285714', 16), // 53.57%
},
],
leftOrdersTakerAssetFilledAmounts: [constants.ZERO_AMOUNT],
rightOrdersTakerAssetFilledAmounts: [constants.ZERO_AMOUNT],
matchIndices: [[0, 0]],
shouldMaximallyFill: true,
});
});
it('should correctly match one left order to two right orders, where the last should not be touched', async () => {
await testBatchMatchOrdersAsync({
leftOrders: [
{
makerAssetAmount: new BigNumber(2),
takerAssetAmount: new BigNumber(1),
},
],
rightOrders: [
{
makerAssetAmount: new BigNumber(1),
takerAssetAmount: new BigNumber(2),
},
{
makerAssetAmount: new BigNumber(1),
takerAssetAmount: new BigNumber(2),
},
],
expectedTransferAmounts: [
{
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(2),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: new BigNumber(1),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Taker
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
},
],
leftOrdersTakerAssetFilledAmounts: [constants.ZERO_AMOUNT],
rightOrdersTakerAssetFilledAmounts: [constants.ZERO_AMOUNT, constants.ZERO_AMOUNT],
matchIndices: [[0, 0]],
shouldMaximallyFill: true,
});
});
it('should correctly fill all four orders in three matches', async () => {
await testBatchMatchOrdersAsync({
leftOrders: [
{
makerAssetAmount: new BigNumber(2),
takerAssetAmount: new BigNumber(1),
},
{
makerAssetAmount: new BigNumber(72),
takerAssetAmount: new BigNumber(36),
},
],
rightOrders: [
{
makerAssetAmount: new BigNumber(15),
takerAssetAmount: new BigNumber(30),
},
{
makerAssetAmount: new BigNumber(22),
takerAssetAmount: new BigNumber(44),
},
],
expectedTransferAmounts: [
{
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(2),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: new BigNumber(1),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount('6.6666666666666666', 16), // 6.66%
// Taker
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount('6.6666666666666666', 16), // 6.66%
},
{
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(28),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount('38.8888888888888888', 16), // 38.88%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: new BigNumber(14),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount('93.3333333333333333', 16), // 93.33%
// Taker
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount('38.8888888888888888', 16), // 38.88%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount('93.3333333333333333', 16), // 93.33%
},
{
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(44),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount('61.1111111111111111', 16), // 61.11%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: new BigNumber(22),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Taker
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount('61.1111111111111111', 16), // 61.11%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
},
],
leftOrdersTakerAssetFilledAmounts: [constants.ZERO_AMOUNT, constants.ZERO_AMOUNT],
rightOrdersTakerAssetFilledAmounts: [constants.ZERO_AMOUNT, constants.ZERO_AMOUNT],
matchIndices: [[0, 0], [1, 0], [1, 1]],
shouldMaximallyFill: true,
});
});
});
describe('token sanity checks', () => {
it('should be able to match ERC721 tokens with ERC1155 tokens', async () => {
const leftMakerAssetData = encodeERC1155AssetData(
deployment.tokens.erc1155[0].address,
[leftId],
[new BigNumber(1)],
'0x',
);
const rightMakerAssetData = encodeERC721AssetData(deployment.tokens.erc721[0].address, rightId);
const signedOrderLeft = await makerLeft.signOrderAsync({
makerAssetAmount: new BigNumber(4),
takerAssetAmount: new BigNumber(1),
makerAssetData: leftMakerAssetData,
takerAssetData: rightMakerAssetData,
});
const signedOrderRight = await makerRight.signOrderAsync({
makerAssetAmount: new BigNumber(1),
takerAssetAmount: new BigNumber(4),
makerAssetData: rightMakerAssetData,
takerAssetData: leftMakerAssetData,
});
const expectedTransferAmounts = {
// Left Maker
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(4),
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Right Maker
rightMakerAssetSoldByRightMakerAmount: new BigNumber(1),
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
// Taker
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
leftProtocolFeePaidByTakerInEthAmount: DeploymentManager.protocolFee,
rightProtocolFeePaidByTakerInEthAmount: DeploymentManager.protocolFee,
};
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
{
leftOrder: signedOrderLeft,
rightOrder: signedOrderRight,
},
expectedTransferAmounts,
matcher.address,
DeploymentManager.protocolFee.times(2),
false,
);
});
});
});
// tslint:disable-line:max-file-line-count