1084 lines
55 KiB
TypeScript
1084 lines
55 KiB
TypeScript
import { encodeERC20AssetData } from '@0x/contracts-asset-proxy';
|
|
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
|
|
import { ExchangeRevertErrors } from '@0x/contracts-exchange';
|
|
import { ReferenceFunctions as LibReferenceFunctions } from '@0x/contracts-exchange-libs';
|
|
import { blockchainTests, constants, expect, orderHashUtils, toBaseUnitAmount } from '@0x/contracts-test-utils';
|
|
import { OrderStatus } 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, TestMatchOrdersArgs, testMatchOrdersAsync } from './match_order_tester';
|
|
|
|
const { isRoundingErrorCeil, isRoundingErrorFloor } = LibReferenceFunctions;
|
|
|
|
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;
|
|
|
|
let defaultMatchOrdersArgs: TestMatchOrdersArgs;
|
|
|
|
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);
|
|
|
|
defaultMatchOrdersArgs = {
|
|
env,
|
|
matchOrderTester,
|
|
makerLeft,
|
|
makerRight,
|
|
leftOrder: constants.STATIC_ORDER_PARAMS,
|
|
rightOrder: constants.STATIC_ORDER_PARAMS,
|
|
matcherAddress: matcher.address,
|
|
expectedTransferAmounts: {},
|
|
withMaximalFill: false,
|
|
};
|
|
});
|
|
|
|
after(async () => {
|
|
Actor.reset();
|
|
});
|
|
|
|
describe('matchOrders', () => {
|
|
// TODO: Should be refactored to use `testMatchOrdersAsync` or moved to unit tests
|
|
it('should transfer correct amounts when right order is fully filled and values pass isRoundingErrorFloor but fail isRoundingErrorCeil', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await makerLeft.signOrderAsync({
|
|
makerAssetAmount: new BigNumber(17),
|
|
takerAssetAmount: new BigNumber(98),
|
|
});
|
|
const signedOrderRight = await makerRight.signOrderAsync({
|
|
makerAssetAmount: new BigNumber(75),
|
|
takerAssetAmount: new BigNumber(13),
|
|
});
|
|
|
|
// Assert is rounding error ceil & not rounding error floor
|
|
// These assertions are taken from MixinMatchOrders::calculateMatchedFillResults
|
|
// The rounding error is derived by computing how much the left maker will sell.
|
|
const numerator = signedOrderLeft.makerAssetAmount;
|
|
const denominator = signedOrderLeft.takerAssetAmount;
|
|
const target = signedOrderRight.makerAssetAmount;
|
|
const _isRoundingErrorCeil = isRoundingErrorCeil(numerator, denominator, target);
|
|
expect(_isRoundingErrorCeil).to.be.true();
|
|
const _isRoundingErrorFloor = isRoundingErrorFloor(numerator, denominator, target);
|
|
expect(_isRoundingErrorFloor).to.be.false();
|
|
|
|
// Match signedOrderLeft with signedOrderRight
|
|
// Note that the left maker received a slightly better sell price.
|
|
// This is intentional; see note in MixinMatchOrders.calculateMatchedFillResults.
|
|
// Because the left maker received a slightly more favorable sell price, the fee
|
|
// paid by the left taker is slightly higher than that paid by the left maker.
|
|
// Fees can be thought of as a tax paid by the seller, derived from the sale price.
|
|
const 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%
|
|
leftProtocolFeePaidByTakerInEthAmount: DeploymentManager.protocolFee,
|
|
rightProtocolFeePaidByTakerInEthAmount: DeploymentManager.protocolFee,
|
|
};
|
|
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
expectedTransferAmounts,
|
|
matcher.address,
|
|
DeploymentManager.protocolFee.times(2),
|
|
false,
|
|
);
|
|
});
|
|
|
|
// TODO: Should be refactored to use `testMatchOrdersAsync` or moved to unit tests
|
|
it('should transfer correct amounts when left order is fully filled and values pass isRoundingErrorCeil but fail isRoundingErrorFloor', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await makerLeft.signOrderAsync({
|
|
makerAssetAmount: new BigNumber(15),
|
|
takerAssetAmount: new BigNumber(90),
|
|
});
|
|
const signedOrderRight = await makerRight.signOrderAsync({
|
|
makerAssetAmount: new BigNumber(97),
|
|
takerAssetAmount: new BigNumber(14),
|
|
});
|
|
|
|
// Assert is rounding error floor & not rounding error ceil
|
|
// These assertions are taken from MixinMatchOrders::calculateMatchedFillResults
|
|
// The rounding error is derived computating how much the right maker will buy.
|
|
const numerator = signedOrderRight.takerAssetAmount;
|
|
const denominator = signedOrderRight.makerAssetAmount;
|
|
const target = signedOrderLeft.takerAssetAmount;
|
|
const _isRoundingErrorFloor = isRoundingErrorFloor(numerator, denominator, target);
|
|
expect(_isRoundingErrorFloor).to.be.true();
|
|
const _isRoundingErrorCeil = isRoundingErrorCeil(numerator, denominator, target);
|
|
expect(_isRoundingErrorCeil).to.be.false();
|
|
|
|
// Match signedOrderLeft isRoundingErrorFloor right maker received a slightly better purchase price.
|
|
// This is intentional; see note in MixinMatchOrders.calculateMatchedFillResults.
|
|
// Because the right maker received a slightly more favorable buy price, the fee
|
|
// paid by the right taker is slightly higher than that paid by the right maker.
|
|
// Fees can be thought of as a tax paid by the seller, derived from the sale price.
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(15),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
leftMakerAssetBoughtByRightMakerAmount: new BigNumber(13),
|
|
rightMakerAssetSoldByRightMakerAmount: new BigNumber(90),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount('92.7835051546391752', 16), // 92.78%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: new BigNumber(2),
|
|
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount('92.8571428571428571', 16), // 92.85%
|
|
leftProtocolFeePaidByTakerInEthAmount: DeploymentManager.protocolFee,
|
|
rightProtocolFeePaidByTakerInEthAmount: DeploymentManager.protocolFee,
|
|
};
|
|
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
expectedTransferAmounts,
|
|
matcher.address,
|
|
DeploymentManager.protocolFee.times(2),
|
|
false,
|
|
);
|
|
});
|
|
|
|
it('should give right maker a better buy price when rounding', async () => {
|
|
await testMatchOrdersAsync({
|
|
...defaultMatchOrdersArgs,
|
|
leftOrder: {
|
|
makerAssetAmount: new BigNumber(16),
|
|
takerAssetAmount: new BigNumber(22),
|
|
},
|
|
rightOrder: {
|
|
makerAssetAmount: new BigNumber(83),
|
|
takerAssetAmount: new BigNumber(49),
|
|
},
|
|
expectedTransferAmounts: {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(16),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: new BigNumber(22),
|
|
leftMakerAssetBoughtByRightMakerAmount: new BigNumber(13),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount('26.5060240963855421', 16), // 26.506%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: new BigNumber(3),
|
|
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount('26.5306122448979591', 16), // 26.531%
|
|
},
|
|
});
|
|
});
|
|
|
|
it('should give left maker a better sell price when rounding', async () => {
|
|
await testMatchOrdersAsync({
|
|
...defaultMatchOrdersArgs,
|
|
leftOrder: {
|
|
makerAssetAmount: new BigNumber(12),
|
|
takerAssetAmount: new BigNumber(97),
|
|
},
|
|
rightOrder: {
|
|
makerAssetAmount: new BigNumber(89),
|
|
takerAssetAmount: new BigNumber(1),
|
|
},
|
|
expectedTransferAmounts: {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(11),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount('91.6666666666666666', 16), // 91.6%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: new BigNumber(89),
|
|
leftMakerAssetBoughtByRightMakerAmount: new BigNumber(1),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: new BigNumber(10),
|
|
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount('91.7525773195876288', 16), // 91.75%
|
|
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
},
|
|
});
|
|
});
|
|
|
|
it('should give right maker and right taker a favorable fee price when rounding', async () => {
|
|
await testMatchOrdersAsync({
|
|
...defaultMatchOrdersArgs,
|
|
leftOrder: {
|
|
makerAssetAmount: new BigNumber(16),
|
|
takerAssetAmount: new BigNumber(22),
|
|
},
|
|
rightOrder: {
|
|
makerAssetAmount: new BigNumber(83),
|
|
takerAssetAmount: new BigNumber(49),
|
|
makerFee: new BigNumber(10000),
|
|
takerFee: new BigNumber(10000),
|
|
},
|
|
expectedTransferAmounts: {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(16),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: new BigNumber(22),
|
|
leftMakerAssetBoughtByRightMakerAmount: new BigNumber(13),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: new BigNumber(2650), // 2650.6 rounded down to 2650
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: new BigNumber(3),
|
|
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: new BigNumber(2653), // 2653.1 rounded down to 2653
|
|
},
|
|
});
|
|
});
|
|
|
|
it('should give left maker and left taker a favorable fee price when rounding', async () => {
|
|
await testMatchOrdersAsync({
|
|
...defaultMatchOrdersArgs,
|
|
leftOrder: {
|
|
makerAssetAmount: new BigNumber(12),
|
|
takerAssetAmount: new BigNumber(97),
|
|
makerFee: new BigNumber(10000),
|
|
takerFee: new BigNumber(10000),
|
|
},
|
|
rightOrder: {
|
|
makerAssetAmount: new BigNumber(89),
|
|
takerAssetAmount: new BigNumber(1),
|
|
},
|
|
expectedTransferAmounts: {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(11),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: new BigNumber(9166), // 9166.6 rounded down to 9166
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: new BigNumber(89),
|
|
leftMakerAssetBoughtByRightMakerAmount: new BigNumber(1),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: new BigNumber(10),
|
|
leftTakerFeeAssetPaidByTakerAmount: new BigNumber(9175), // 9175.2 rounded down to 9175
|
|
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
},
|
|
});
|
|
});
|
|
|
|
it(`should transfer correct amounts when right order fill amount deviates
|
|
from amount derived by \`Exchange.fillOrder\``, async () => {
|
|
await testMatchOrdersAsync({
|
|
...defaultMatchOrdersArgs,
|
|
leftOrder: {
|
|
makerAssetAmount: new BigNumber(1000),
|
|
takerAssetAmount: new BigNumber(1005),
|
|
},
|
|
rightOrder: {
|
|
makerAssetAmount: new BigNumber(2126),
|
|
takerAssetAmount: new BigNumber(1063),
|
|
},
|
|
expectedTransferAmounts: {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: new BigNumber(1000),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
// Notes:
|
|
// i.
|
|
// The left order is fully filled by the right order, so the right maker must sell 1005 units of their asset to the left maker.
|
|
// By selling 1005 units, the right maker should theoretically receive 502.5 units of the left maker's asset.
|
|
// Since the transfer amount must be an integer, this value must be rounded down to 502 or up to 503.
|
|
// ii.
|
|
// If the right order were filled via `Exchange.fillOrder` the respective fill amounts would be [1004, 502] or [1006, 503].
|
|
// It follows that we cannot trigger a sale of 1005 units of the right maker's asset through `Exchange.fillOrder`.
|
|
// iii.
|
|
// For an optimal match, the algorithm must choose either [1005, 502] or [1005, 503] as fill amounts for the right order.
|
|
// The algorithm favors the right maker when the exchange rate must be rounded, so the final fill for the right order is [1005, 503].
|
|
// iv.
|
|
// The right maker fee differs from the right taker fee because their exchange rate differs.
|
|
// The right maker always receives the better exchange and fee price.
|
|
rightMakerAssetSoldByRightMakerAmount: new BigNumber(1005),
|
|
leftMakerAssetBoughtByRightMakerAmount: new BigNumber(503),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount('47.2718720602069614', 16), // 47.27%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: new BigNumber(497),
|
|
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount('47.3189087488240827', 16), // 47.31%
|
|
},
|
|
});
|
|
});
|
|
|
|
it('should transfer the correct amounts when orders completely fill each other', async () => {
|
|
await testMatchOrdersAsync({
|
|
...defaultMatchOrdersArgs,
|
|
leftOrder: {
|
|
makerAssetAmount: toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: toBaseUnitAmount(10, 18),
|
|
},
|
|
rightOrder: {
|
|
makerAssetAmount: toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: toBaseUnitAmount(2, 18),
|
|
},
|
|
expectedTransferAmounts: {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
},
|
|
});
|
|
});
|
|
|
|
it(`should transfer the correct amounts when orders completely fill each
|
|
other and taker doesn't take a profit`, async () => {
|
|
await testMatchOrdersAsync({
|
|
...defaultMatchOrdersArgs,
|
|
leftOrder: {
|
|
makerAssetAmount: toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: toBaseUnitAmount(10, 18),
|
|
},
|
|
rightOrder: {
|
|
makerAssetAmount: toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: toBaseUnitAmount(5, 18),
|
|
},
|
|
expectedTransferAmounts: {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(10, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
},
|
|
});
|
|
});
|
|
|
|
it(`should transfer the correct amounts when left order is completely filled
|
|
and right order is partially filled`, async () => {
|
|
await testMatchOrdersAsync({
|
|
...defaultMatchOrdersArgs,
|
|
leftOrder: {
|
|
makerAssetAmount: toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: toBaseUnitAmount(10, 18),
|
|
},
|
|
rightOrder: {
|
|
makerAssetAmount: toBaseUnitAmount(20, 18),
|
|
takerAssetAmount: toBaseUnitAmount(4, 18),
|
|
},
|
|
expectedTransferAmounts: {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(50, 16), // 50%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(50, 16), // 50%
|
|
},
|
|
});
|
|
});
|
|
|
|
it(`should transfer the correct amounts when right order is completely filled
|
|
and left order is partially filled`, async () => {
|
|
await testMatchOrdersAsync({
|
|
...defaultMatchOrdersArgs,
|
|
leftOrder: {
|
|
makerAssetAmount: toBaseUnitAmount(50, 18),
|
|
takerAssetAmount: toBaseUnitAmount(100, 18),
|
|
},
|
|
rightOrder: {
|
|
makerAssetAmount: toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: toBaseUnitAmount(2, 18),
|
|
},
|
|
expectedTransferAmounts: {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(10, 16), // 10%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(10, 16), // 10%
|
|
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
},
|
|
});
|
|
});
|
|
|
|
it('should transfer the correct amounts when consecutive calls are used to completely fill the left order', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await makerLeft.signOrderAsync({
|
|
makerAssetAmount: toBaseUnitAmount(50, 18),
|
|
takerAssetAmount: toBaseUnitAmount(100, 18),
|
|
});
|
|
const signedOrderRight = await makerRight.signOrderAsync({
|
|
makerAssetAmount: toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: toBaseUnitAmount(2, 18),
|
|
});
|
|
// Match orders
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(10, 16), // 10%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(10, 16), // 10%
|
|
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
leftProtocolFeePaidByTakerInEthAmount: DeploymentManager.protocolFee,
|
|
rightProtocolFeePaidByTakerInEthAmount: DeploymentManager.protocolFee,
|
|
};
|
|
// prettier-ignore
|
|
const matchResults = await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
expectedTransferAmounts,
|
|
matcher.address,
|
|
DeploymentManager.protocolFee.times(2),
|
|
false,
|
|
);
|
|
// Construct second right order
|
|
// Note: This order needs makerAssetAmount=90/takerAssetAmount=[anything <= 45] to fully fill the right order.
|
|
// However, we use 100/50 to ensure a partial fill as we want to go down the "left fill"
|
|
// branch in the contract twice for this test.
|
|
const signedOrderRight2 = await makerRight.signOrderAsync({
|
|
makerAssetAmount: toBaseUnitAmount(100, 18),
|
|
takerAssetAmount: toBaseUnitAmount(50, 18),
|
|
});
|
|
// Match signedOrderLeft with signedOrderRight2
|
|
const expectedTransferAmounts2 = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(45, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(90, 16), // 90% (10% paid earlier)
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(90, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(90, 16), // 90%
|
|
// Taker
|
|
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(90, 16), // 90% (10% paid earlier)
|
|
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(90, 16), // 90%
|
|
leftProtocolFeePaidByTakerInEthAmount: DeploymentManager.protocolFee,
|
|
rightProtocolFeePaidByTakerInEthAmount: DeploymentManager.protocolFee,
|
|
};
|
|
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight2,
|
|
leftOrderTakerAssetFilledAmount: matchResults.orders.leftOrderTakerAssetFilledAmount,
|
|
},
|
|
expectedTransferAmounts2,
|
|
matcher.address,
|
|
DeploymentManager.protocolFee.times(2),
|
|
false,
|
|
);
|
|
});
|
|
|
|
it('should transfer the correct amounts when consecutive calls are used to completely fill the right order', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await makerLeft.signOrderAsync({
|
|
makerAssetAmount: toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: toBaseUnitAmount(2, 18),
|
|
});
|
|
const signedOrderRight = await makerRight.signOrderAsync({
|
|
makerAssetAmount: toBaseUnitAmount(50, 18),
|
|
takerAssetAmount: toBaseUnitAmount(100, 18),
|
|
});
|
|
|
|
// Match orders
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(10, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(2, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: toBaseUnitAmount(4, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(4, 16), // 4%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: toBaseUnitAmount(6, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(4, 16), // 4%
|
|
leftProtocolFeePaidByTakerInEthAmount: DeploymentManager.protocolFee,
|
|
rightProtocolFeePaidByTakerInEthAmount: DeploymentManager.protocolFee,
|
|
};
|
|
const matchResults = await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
expectedTransferAmounts,
|
|
matcher.address,
|
|
DeploymentManager.protocolFee.times(2),
|
|
false,
|
|
);
|
|
|
|
// Create second left order
|
|
// Note: This order needs makerAssetAmount=96/takerAssetAmount=48 to fully fill the right order.
|
|
// However, we use 100/50 to ensure a partial fill as we want to go down the "right fill"
|
|
// branch in the contract twice for this test.
|
|
const signedOrderLeft2 = await makerLeft.signOrderAsync({
|
|
makerAssetAmount: toBaseUnitAmount(100, 18),
|
|
takerAssetAmount: toBaseUnitAmount(50, 18),
|
|
});
|
|
|
|
// Match signedOrderLeft2 with signedOrderRight
|
|
const expectedTransferAmounts2 = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(96, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(96, 16), // 96%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(48, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(96, 16), // 96%
|
|
// Taker
|
|
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(96, 16), // 96%
|
|
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(96, 16), // 96%
|
|
leftProtocolFeePaidByTakerInEthAmount: DeploymentManager.protocolFee,
|
|
rightProtocolFeePaidByTakerInEthAmount: DeploymentManager.protocolFee,
|
|
};
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft2,
|
|
rightOrder: signedOrderRight,
|
|
rightOrderTakerAssetFilledAmount: matchResults.orders.rightOrderTakerAssetFilledAmount,
|
|
},
|
|
expectedTransferAmounts2,
|
|
matcher.address,
|
|
DeploymentManager.protocolFee.times(2),
|
|
false,
|
|
);
|
|
});
|
|
|
|
it('should transfer the correct amounts if fee recipient is the same across both matched orders', async () => {
|
|
await testMatchOrdersAsync({
|
|
...defaultMatchOrdersArgs,
|
|
leftOrder: {
|
|
makerAssetAmount: toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: toBaseUnitAmount(10, 18),
|
|
feeRecipientAddress: feeRecipientLeft.address,
|
|
},
|
|
rightOrder: {
|
|
makerAssetAmount: toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: toBaseUnitAmount(2, 18),
|
|
feeRecipientAddress: feeRecipientLeft.address,
|
|
},
|
|
expectedTransferAmounts: {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
},
|
|
});
|
|
});
|
|
|
|
it('should transfer the correct amounts if taker == leftMaker', async () => {
|
|
await testMatchOrdersAsync({
|
|
...defaultMatchOrdersArgs,
|
|
leftOrder: {
|
|
makerAssetAmount: toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: toBaseUnitAmount(10, 18),
|
|
},
|
|
rightOrder: {
|
|
makerAssetAmount: toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: toBaseUnitAmount(2, 18),
|
|
},
|
|
expectedTransferAmounts: {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
},
|
|
matcherAddress: makerLeft.address,
|
|
});
|
|
});
|
|
|
|
it('should transfer the correct amounts if taker == rightMaker', async () => {
|
|
await testMatchOrdersAsync({
|
|
...defaultMatchOrdersArgs,
|
|
leftOrder: {
|
|
makerAssetAmount: toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: toBaseUnitAmount(10, 18),
|
|
},
|
|
rightOrder: {
|
|
makerAssetAmount: toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: toBaseUnitAmount(2, 18),
|
|
},
|
|
expectedTransferAmounts: {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
},
|
|
matcherAddress: makerRight.address,
|
|
});
|
|
});
|
|
|
|
it('should transfer the correct amounts if taker == leftFeeRecipient', async () => {
|
|
await testMatchOrdersAsync({
|
|
...defaultMatchOrdersArgs,
|
|
leftOrder: {
|
|
makerAssetAmount: toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: toBaseUnitAmount(10, 18),
|
|
},
|
|
rightOrder: {
|
|
makerAssetAmount: toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: toBaseUnitAmount(2, 18),
|
|
},
|
|
expectedTransferAmounts: {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
},
|
|
matcherAddress: feeRecipientLeft.address,
|
|
});
|
|
});
|
|
|
|
it('should transfer the correct amounts if taker == rightFeeRecipient', async () => {
|
|
await testMatchOrdersAsync({
|
|
...defaultMatchOrdersArgs,
|
|
leftOrder: {
|
|
makerAssetAmount: toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: toBaseUnitAmount(10, 18),
|
|
},
|
|
rightOrder: {
|
|
makerAssetAmount: toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: toBaseUnitAmount(2, 18),
|
|
},
|
|
expectedTransferAmounts: {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
},
|
|
matcherAddress: feeRecipientRight.address,
|
|
});
|
|
});
|
|
|
|
it(`should transfer the correct amounts if leftMaker == leftFeeRecipient
|
|
&& rightMaker == rightFeeRecipient`, async () => {
|
|
await testMatchOrdersAsync({
|
|
...defaultMatchOrdersArgs,
|
|
leftOrder: {
|
|
makerAssetAmount: toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: toBaseUnitAmount(10, 18),
|
|
feeRecipientAddress: makerLeft.address,
|
|
},
|
|
rightOrder: {
|
|
makerAssetAmount: toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: toBaseUnitAmount(2, 18),
|
|
feeRecipientAddress: makerRight.address,
|
|
},
|
|
expectedTransferAmounts: {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
},
|
|
});
|
|
});
|
|
|
|
it(`should transfer the correct amounts if leftMaker == leftFeeRecipient
|
|
&& leftMakerFeeAsset == leftTakerAsset`, async () => {
|
|
await testMatchOrdersAsync({
|
|
...defaultMatchOrdersArgs,
|
|
leftOrder: {
|
|
makerAssetAmount: toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: toBaseUnitAmount(10, 18),
|
|
makerFeeAssetData: makerAssetDataRight,
|
|
feeRecipientAddress: makerLeft.address,
|
|
},
|
|
rightOrder: {
|
|
makerAssetAmount: toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: toBaseUnitAmount(2, 18),
|
|
},
|
|
expectedTransferAmounts: {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
},
|
|
});
|
|
});
|
|
|
|
it(`should transfer the correct amounts if rightMaker == rightFeeRecipient
|
|
&& rightMakerFeeAsset == rightTakerAsset`, async () => {
|
|
await testMatchOrdersAsync({
|
|
...defaultMatchOrdersArgs,
|
|
leftOrder: {
|
|
makerAssetAmount: toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: toBaseUnitAmount(10, 18),
|
|
},
|
|
rightOrder: {
|
|
makerAssetAmount: toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: toBaseUnitAmount(2, 18),
|
|
makerFeeAssetData: makerAssetDataLeft,
|
|
feeRecipientAddress: makerRight.address,
|
|
},
|
|
expectedTransferAmounts: {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
},
|
|
});
|
|
});
|
|
|
|
it(`should transfer the correct amounts if rightMaker == rightFeeRecipient && rightTakerAsset == rightMakerFeeAsset
|
|
&& leftMaker == leftFeeRecipient && leftTakerAsset == leftMakerFeeAsset`, async () => {
|
|
await testMatchOrdersAsync({
|
|
...defaultMatchOrdersArgs,
|
|
leftOrder: {
|
|
makerAssetAmount: toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: toBaseUnitAmount(10, 18),
|
|
makerFeeAssetData: makerAssetDataRight,
|
|
feeRecipientAddress: makerLeft.address,
|
|
},
|
|
rightOrder: {
|
|
makerAssetAmount: toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: toBaseUnitAmount(2, 18),
|
|
makerFeeAssetData: makerAssetDataRight,
|
|
feeRecipientAddress: makerRight.address,
|
|
},
|
|
expectedTransferAmounts: {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100%
|
|
},
|
|
});
|
|
});
|
|
|
|
it('should revert if left order is not fillable', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await makerLeft.signOrderAsync({
|
|
makerAssetAmount: toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: toBaseUnitAmount(10, 18),
|
|
});
|
|
const signedOrderRight = await makerRight.signOrderAsync({
|
|
makerAssetAmount: toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: toBaseUnitAmount(2, 18),
|
|
});
|
|
const orderHashHexLeft = orderHashUtils.getOrderHashHex(signedOrderLeft);
|
|
|
|
// Cancel left order
|
|
await makerLeft.cancelOrderAsync(signedOrderLeft);
|
|
|
|
// Match orders
|
|
const expectedError = new ExchangeRevertErrors.OrderStatusError(orderHashHexLeft, OrderStatus.Cancelled);
|
|
const tx = deployment.exchange
|
|
.matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature)
|
|
.awaitTransactionSuccessAsync({ from: matcher.address });
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('should revert if right order is not fillable', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await makerLeft.signOrderAsync({
|
|
makerAssetAmount: toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: toBaseUnitAmount(10, 18),
|
|
});
|
|
const signedOrderRight = await makerRight.signOrderAsync({
|
|
makerAssetAmount: toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: toBaseUnitAmount(2, 18),
|
|
});
|
|
const orderHashHexRight = orderHashUtils.getOrderHashHex(signedOrderRight);
|
|
|
|
// Cancel right order
|
|
await makerRight.cancelOrderAsync(signedOrderRight);
|
|
|
|
// Match orders
|
|
const expectedError = new ExchangeRevertErrors.OrderStatusError(orderHashHexRight, OrderStatus.Cancelled);
|
|
const tx = deployment.exchange
|
|
.matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature)
|
|
.awaitTransactionSuccessAsync({ from: matcher.address });
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('should revert if there is not a positive spread', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await makerLeft.signOrderAsync({
|
|
makerAssetAmount: toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: toBaseUnitAmount(100, 18),
|
|
});
|
|
const signedOrderRight = await makerRight.signOrderAsync({
|
|
makerAssetAmount: toBaseUnitAmount(1, 18),
|
|
takerAssetAmount: toBaseUnitAmount(200, 18),
|
|
});
|
|
const orderHashHexLeft = orderHashUtils.getOrderHashHex(signedOrderLeft);
|
|
const orderHashHexRight = orderHashUtils.getOrderHashHex(signedOrderRight);
|
|
|
|
// Match orders
|
|
const expectedError = new ExchangeRevertErrors.NegativeSpreadError(orderHashHexLeft, orderHashHexRight);
|
|
const tx = deployment.exchange
|
|
.matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature)
|
|
.awaitTransactionSuccessAsync({ from: matcher.address });
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('should revert if the left maker asset is not equal to the right taker asset ', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await makerLeft.signOrderAsync({
|
|
makerAssetAmount: toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: toBaseUnitAmount(10, 18),
|
|
});
|
|
const signedOrderRight = await makerRight.signOrderAsync({
|
|
takerAssetData: encodeERC20AssetData(makerAssetRight.address),
|
|
makerAssetAmount: toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: toBaseUnitAmount(2, 18),
|
|
});
|
|
|
|
// We are assuming assetData fields of the right order are the
|
|
// reverse of the left order, rather than checking equality. This
|
|
// saves a bunch of gas, but as a result if the assetData fields are
|
|
// off then the failure ends up happening at signature validation
|
|
const reconstructedOrderRight = {
|
|
...signedOrderRight,
|
|
takerAssetData: signedOrderLeft.makerAssetData,
|
|
};
|
|
const orderHashHex = orderHashUtils.getOrderHashHex(reconstructedOrderRight);
|
|
const expectedError = new ExchangeRevertErrors.SignatureError(
|
|
ExchangeRevertErrors.SignatureErrorCode.BadOrderSignature,
|
|
orderHashHex,
|
|
signedOrderRight.makerAddress,
|
|
signedOrderRight.signature,
|
|
);
|
|
|
|
// Match orders
|
|
const tx = deployment.exchange
|
|
.matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature)
|
|
.awaitTransactionSuccessAsync({ from: matcher.address });
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('should revert if the right maker asset is not equal to the left taker asset', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await makerLeft.signOrderAsync({
|
|
takerAssetData: encodeERC20AssetData(makerAssetLeft.address),
|
|
makerAssetAmount: toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: toBaseUnitAmount(10, 18),
|
|
});
|
|
const signedOrderRight = await makerRight.signOrderAsync({
|
|
makerAssetAmount: toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: toBaseUnitAmount(2, 18),
|
|
});
|
|
const reconstructedOrderRight = {
|
|
...signedOrderRight,
|
|
makerAssetData: signedOrderLeft.takerAssetData,
|
|
};
|
|
const orderHashHex = orderHashUtils.getOrderHashHex(reconstructedOrderRight);
|
|
const expectedError = new ExchangeRevertErrors.SignatureError(
|
|
ExchangeRevertErrors.SignatureErrorCode.BadOrderSignature,
|
|
orderHashHex,
|
|
signedOrderRight.makerAddress,
|
|
signedOrderRight.signature,
|
|
);
|
|
// Match orders
|
|
const tx = deployment.exchange
|
|
.matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature)
|
|
.awaitTransactionSuccessAsync({ from: matcher.address });
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
});
|
|
});
|
|
// tslint:disable-line:max-file-line-count
|