3714 lines
196 KiB
TypeScript
3714 lines
196 KiB
TypeScript
import {
|
|
artifacts as assetProxyArtifacts,
|
|
ERC1155ProxyContract,
|
|
ERC1155ProxyWrapper,
|
|
ERC20ProxyContract,
|
|
ERC20Wrapper,
|
|
ERC721ProxyContract,
|
|
ERC721Wrapper,
|
|
MultiAssetProxyContract,
|
|
} from '@0x/contracts-asset-proxy';
|
|
import { ERC1155Contract as ERC1155TokenContract, Erc1155Wrapper as ERC1155Wrapper } from '@0x/contracts-erc1155';
|
|
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
|
|
import { DummyERC721TokenContract } from '@0x/contracts-erc721';
|
|
import { ReferenceFunctions as LibReferenceFunctions } from '@0x/contracts-exchange-libs';
|
|
import {
|
|
chaiSetup,
|
|
constants,
|
|
OrderFactory,
|
|
orderUtils,
|
|
provider,
|
|
txDefaults,
|
|
web3Wrapper,
|
|
} from '@0x/contracts-test-utils';
|
|
import { BlockchainLifecycle } from '@0x/dev-utils';
|
|
import { assetDataUtils, ExchangeRevertErrors, orderHashUtils } from '@0x/order-utils';
|
|
import { OrderStatus, SignedOrder } from '@0x/types';
|
|
import { BigNumber, providerUtils } from '@0x/utils';
|
|
import { Web3Wrapper } from '@0x/web3-wrapper';
|
|
import * as chai from 'chai';
|
|
import * as _ from 'lodash';
|
|
|
|
import { artifacts, ExchangeContract, ExchangeWrapper } from '../src';
|
|
|
|
import { MatchOrderTester, TokenBalances } from './utils/match_order_tester';
|
|
|
|
const ZERO = new BigNumber(0);
|
|
const ONE = new BigNumber(1);
|
|
const TWO = new BigNumber(2);
|
|
const { isRoundingErrorCeil, isRoundingErrorFloor } = LibReferenceFunctions;
|
|
|
|
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
|
chaiSetup.configure();
|
|
const expect = chai.expect;
|
|
|
|
// Reduce the number of tokens to deploy to speed up tests, since we don't need
|
|
// so many.
|
|
constants.NUM_DUMMY_ERC721_TO_DEPLOY = 1;
|
|
constants.NUM_DUMMY_ERC1155_CONTRACTS_TO_DEPLOY = 1;
|
|
|
|
describe('matchOrders', () => {
|
|
let chainId: number;
|
|
let makerAddressLeft: string;
|
|
let makerAddressRight: string;
|
|
let owner: string;
|
|
let takerAddress: string;
|
|
let feeRecipientAddressLeft: string;
|
|
let feeRecipientAddressRight: string;
|
|
|
|
let erc20Tokens: DummyERC20TokenContract[];
|
|
let erc721Token: DummyERC721TokenContract;
|
|
let erc1155Token: ERC1155TokenContract;
|
|
let exchange: ExchangeContract;
|
|
let erc20Proxy: ERC20ProxyContract;
|
|
let erc721Proxy: ERC721ProxyContract;
|
|
let erc1155Proxy: ERC1155ProxyContract;
|
|
let erc1155ProxyWrapper: ERC1155ProxyWrapper;
|
|
|
|
let exchangeWrapper: ExchangeWrapper;
|
|
let erc20Wrapper: ERC20Wrapper;
|
|
let erc721Wrapper: ERC721Wrapper;
|
|
let erc1155Wrapper: ERC1155Wrapper;
|
|
let orderFactoryLeft: OrderFactory;
|
|
let orderFactoryRight: OrderFactory;
|
|
|
|
let tokenBalances: TokenBalances;
|
|
|
|
let defaultERC20MakerAssetAddress: string;
|
|
let defaultERC20TakerAssetAddress: string;
|
|
let defaultERC721AssetAddress: string;
|
|
let defaultERC1155AssetAddress: string;
|
|
let defaultFeeTokenAddress: string;
|
|
|
|
let matchOrderTester: MatchOrderTester;
|
|
|
|
before(async () => {
|
|
await blockchainLifecycle.startAsync();
|
|
});
|
|
after(async () => {
|
|
await blockchainLifecycle.revertAsync();
|
|
});
|
|
before(async () => {
|
|
// Get the chain ID.
|
|
chainId = await providerUtils.getChainIdAsync(provider);
|
|
// Create accounts
|
|
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
|
const usedAddresses = ([
|
|
owner,
|
|
makerAddressLeft,
|
|
makerAddressRight,
|
|
takerAddress,
|
|
feeRecipientAddressLeft,
|
|
feeRecipientAddressRight,
|
|
] = accounts);
|
|
const addressesWithBalances = usedAddresses.slice(1);
|
|
// Create wrappers
|
|
erc20Wrapper = new ERC20Wrapper(provider, addressesWithBalances, owner);
|
|
erc721Wrapper = new ERC721Wrapper(provider, addressesWithBalances, owner);
|
|
erc1155ProxyWrapper = new ERC1155ProxyWrapper(provider, addressesWithBalances, owner);
|
|
// Deploy ERC20 token & ERC20 proxy
|
|
const numDummyErc20ToDeploy = 4;
|
|
erc20Tokens = await erc20Wrapper.deployDummyTokensAsync(numDummyErc20ToDeploy, constants.DUMMY_TOKEN_DECIMALS);
|
|
erc20Proxy = await erc20Wrapper.deployProxyAsync();
|
|
await erc20Wrapper.setBalancesAndAllowancesAsync();
|
|
// Deploy ERC721 token and proxy
|
|
[erc721Token] = await erc721Wrapper.deployDummyTokensAsync();
|
|
erc721Proxy = await erc721Wrapper.deployProxyAsync();
|
|
await erc721Wrapper.setBalancesAndAllowancesAsync();
|
|
// Deploy ERC1155 token and proxy
|
|
[erc1155Wrapper] = await erc1155ProxyWrapper.deployDummyContractsAsync();
|
|
erc1155Token = (erc1155Wrapper.getContract() as any) as ERC1155TokenContract;
|
|
erc1155Proxy = await erc1155ProxyWrapper.deployProxyAsync();
|
|
await erc1155ProxyWrapper.setBalancesAndAllowancesAsync();
|
|
// Deploy MultiAssetProxy.
|
|
const multiAssetProxyContract = await MultiAssetProxyContract.deployFrom0xArtifactAsync(
|
|
assetProxyArtifacts.MultiAssetProxy,
|
|
provider,
|
|
txDefaults,
|
|
{},
|
|
);
|
|
// Depoy exchange
|
|
exchange = await ExchangeContract.deployFrom0xArtifactAsync(
|
|
artifacts.Exchange,
|
|
provider,
|
|
txDefaults,
|
|
{},
|
|
new BigNumber(chainId),
|
|
);
|
|
exchangeWrapper = new ExchangeWrapper(exchange, provider);
|
|
await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner);
|
|
await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner);
|
|
await exchangeWrapper.registerAssetProxyAsync(erc1155Proxy.address, owner);
|
|
await exchangeWrapper.registerAssetProxyAsync(multiAssetProxyContract.address, owner);
|
|
// Authorize proxies.
|
|
await erc20Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
|
exchange.address,
|
|
{ from: owner },
|
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
);
|
|
await erc721Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
|
exchange.address,
|
|
{ from: owner },
|
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
);
|
|
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
|
exchange.address,
|
|
{ from: owner },
|
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
);
|
|
await multiAssetProxyContract.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
|
exchange.address,
|
|
{ from: owner },
|
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
);
|
|
await erc20Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
|
multiAssetProxyContract.address,
|
|
{ from: owner },
|
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
);
|
|
await erc721Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
|
multiAssetProxyContract.address,
|
|
{ from: owner },
|
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
);
|
|
await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
|
|
multiAssetProxyContract.address,
|
|
{ from: owner },
|
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
);
|
|
await multiAssetProxyContract.registerAssetProxy.awaitTransactionSuccessAsync(
|
|
erc20Proxy.address,
|
|
{ from: owner },
|
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
);
|
|
await multiAssetProxyContract.registerAssetProxy.awaitTransactionSuccessAsync(
|
|
erc721Proxy.address,
|
|
{ from: owner },
|
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
);
|
|
await multiAssetProxyContract.registerAssetProxy.awaitTransactionSuccessAsync(
|
|
erc1155Proxy.address,
|
|
{ from: owner },
|
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
);
|
|
|
|
// Set default addresses
|
|
defaultERC20MakerAssetAddress = erc20Tokens[0].address;
|
|
defaultERC20TakerAssetAddress = erc20Tokens[1].address;
|
|
defaultFeeTokenAddress = erc20Tokens[2].address;
|
|
defaultERC721AssetAddress = erc721Token.address;
|
|
defaultERC1155AssetAddress = erc1155Token.address;
|
|
const domain = {
|
|
verifyingContractAddress: exchange.address,
|
|
chainId,
|
|
};
|
|
// Create default order parameters
|
|
const defaultOrderParamsLeft = {
|
|
...constants.STATIC_ORDER_PARAMS,
|
|
makerAddress: makerAddressLeft,
|
|
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
makerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultFeeTokenAddress),
|
|
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultFeeTokenAddress),
|
|
feeRecipientAddress: feeRecipientAddressLeft,
|
|
domain,
|
|
};
|
|
const defaultOrderParamsRight = {
|
|
...constants.STATIC_ORDER_PARAMS,
|
|
makerAddress: makerAddressRight,
|
|
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
makerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultFeeTokenAddress),
|
|
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultFeeTokenAddress),
|
|
feeRecipientAddress: feeRecipientAddressRight,
|
|
domain,
|
|
};
|
|
const privateKeyLeft = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressLeft)];
|
|
orderFactoryLeft = new OrderFactory(privateKeyLeft, defaultOrderParamsLeft);
|
|
const privateKeyRight = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressRight)];
|
|
orderFactoryRight = new OrderFactory(privateKeyRight, defaultOrderParamsRight);
|
|
// Create match order tester
|
|
matchOrderTester = new MatchOrderTester(exchangeWrapper, erc20Wrapper, erc721Wrapper, erc1155ProxyWrapper);
|
|
tokenBalances = await matchOrderTester.getBalancesAsync();
|
|
});
|
|
beforeEach(async () => {
|
|
await blockchainLifecycle.startAsync();
|
|
});
|
|
afterEach(async () => {
|
|
await blockchainLifecycle.revertAsync();
|
|
});
|
|
describe('matchOrders', () => {
|
|
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 orderFactoryLeft.newSignedOrderAsync({
|
|
makerAddress: makerAddressLeft,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(17, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(98, 0),
|
|
feeRecipientAddress: feeRecipientAddressLeft,
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAddress: makerAddressRight,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(75, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(13, 0),
|
|
feeRecipientAddress: feeRecipientAddressRight,
|
|
});
|
|
// Assert is rounding error ceil & not rounding error floor
|
|
// These assertions are taken from MixinMatchOrders::calculateMatchedFillResults
|
|
// The rounding error is derived computating 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: Web3Wrapper.toBaseUnitAmount(13, 0),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('76.4705882352941176'),
|
|
16,
|
|
), // 76.47%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(75, 0),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('76.5306122448979591'),
|
|
16,
|
|
), // 76.53%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
false,
|
|
);
|
|
});
|
|
|
|
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 orderFactoryLeft.newSignedOrderAsync({
|
|
makerAddress: makerAddressLeft,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(15, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(90, 0),
|
|
feeRecipientAddress: feeRecipientAddressLeft,
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAddress: makerAddressRight,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(97, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(14, 0),
|
|
feeRecipientAddress: feeRecipientAddressRight,
|
|
});
|
|
// 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: Web3Wrapper.toBaseUnitAmount(15, 0),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(13, 0),
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(90, 0),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('92.7835051546391752'),
|
|
16,
|
|
), // 92.78%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(2, 0),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('92.8571428571428571'),
|
|
16,
|
|
), // 92.85%
|
|
};
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
false,
|
|
);
|
|
});
|
|
|
|
it('Should give right maker a better buy price when rounding', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAddress: makerAddressLeft,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(16, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(22, 0),
|
|
feeRecipientAddress: feeRecipientAddressLeft,
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAddress: makerAddressRight,
|
|
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(83, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(49, 0),
|
|
feeRecipientAddress: feeRecipientAddressRight,
|
|
});
|
|
// Note:
|
|
// The correct price buy price for the right maker would yield (49/83) * 22 = 12.988 units
|
|
// of the left maker asset. This gets rounded up to 13, giving the right maker a better price.
|
|
// Note:
|
|
// The maker/taker fee percentage paid on the right order differs because
|
|
// they received different sale prices. The right maker pays a
|
|
// fee slightly lower than the right taker.
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(16, 0),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(22, 0),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(13, 0),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('26.5060240963855421'),
|
|
16,
|
|
), // 26.506%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(3, 0),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('26.5306122448979591'),
|
|
16,
|
|
), // 26.531%
|
|
};
|
|
// Match signedOrderLeft with signedOrderRight
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
false,
|
|
);
|
|
});
|
|
|
|
it('Should give left maker a better sell price when rounding', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAddress: makerAddressLeft,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(12, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(97, 0),
|
|
feeRecipientAddress: feeRecipientAddressLeft,
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAddress: makerAddressRight,
|
|
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(89, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
|
feeRecipientAddress: feeRecipientAddressRight,
|
|
});
|
|
// Note:
|
|
// The maker/taker fee percentage paid on the left order differs because
|
|
// they received different sale prices. The left maker pays a fee
|
|
// slightly lower than the left taker.
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(11, 0),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('91.6666666666666666'),
|
|
16,
|
|
), // 91.6%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(89, 0),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(10, 0),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('91.7525773195876288'),
|
|
16,
|
|
), // 91.75%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
// Match signedOrderLeft with signedOrderRight
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
false,
|
|
);
|
|
});
|
|
|
|
it('Should give right maker and right taker a favorable fee price when rounding', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAddress: makerAddressLeft,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(16, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(22, 0),
|
|
feeRecipientAddress: feeRecipientAddressLeft,
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAddress: makerAddressRight,
|
|
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(83, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(49, 0),
|
|
feeRecipientAddress: feeRecipientAddressRight,
|
|
makerFee: Web3Wrapper.toBaseUnitAmount(10000, 0),
|
|
takerFee: Web3Wrapper.toBaseUnitAmount(10000, 0),
|
|
});
|
|
// Note:
|
|
// The maker/taker fee percentage paid on the right order differs because
|
|
// they received different sale prices. The right maker pays a
|
|
// fee slightly lower than the right taker.
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(16, 0),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(22, 0),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(13, 0),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(2650, 0), // 2650.6 rounded down tro 2650
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(3, 0),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(2653, 0), // 2653.1 rounded down to 2653
|
|
};
|
|
// Match signedOrderLeft with signedOrderRight
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
false,
|
|
);
|
|
});
|
|
|
|
it('Should give left maker and left taker a favorable fee price when rounding', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAddress: makerAddressLeft,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(12, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(97, 0),
|
|
feeRecipientAddress: feeRecipientAddressLeft,
|
|
makerFee: Web3Wrapper.toBaseUnitAmount(10000, 0),
|
|
takerFee: Web3Wrapper.toBaseUnitAmount(10000, 0),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAddress: makerAddressRight,
|
|
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(89, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
|
feeRecipientAddress: feeRecipientAddressRight,
|
|
});
|
|
// Note:
|
|
// The maker/taker fee percentage paid on the left order differs because
|
|
// they received different sale prices. The left maker pays a
|
|
// fee slightly lower than the left taker.
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(11, 0),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(9166, 0), // 9166.6 rounded down to 9166
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(89, 0),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(10, 0),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(9175, 0), // 9175.2 rounded down to 9175
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
// Match signedOrderLeft with signedOrderRight
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
false,
|
|
);
|
|
});
|
|
|
|
it('Should transfer correct amounts when right order fill amount deviates from amount derived by `Exchange.fillOrder`', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAddress: makerAddressLeft,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(1000, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(1005, 0),
|
|
feeRecipientAddress: feeRecipientAddressLeft,
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAddress: makerAddressRight,
|
|
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(2126, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(1063, 0),
|
|
feeRecipientAddress: feeRecipientAddressRight,
|
|
});
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(1000, 0),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.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: Web3Wrapper.toBaseUnitAmount(1005, 0),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(503, 0),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('47.2718720602069614'),
|
|
16,
|
|
), // 47.27%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(497, 0),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('47.3189087488240827'),
|
|
16,
|
|
), // 47.31%
|
|
};
|
|
// Match signedOrderLeft with signedOrderRight
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
false,
|
|
);
|
|
});
|
|
|
|
it('should transfer the correct amounts when orders completely fill each other', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
});
|
|
// Match signedOrderLeft with signedOrderRight
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
false,
|
|
);
|
|
});
|
|
|
|
it('should transfer the correct amounts when orders completely fill each other and taker doesnt take a profit', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
});
|
|
// Match signedOrderLeft with signedOrderRight
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
// Match signedOrderLeft with signedOrderRight
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
false,
|
|
);
|
|
});
|
|
|
|
it('should transfer the correct amounts when left order is completely filled and right order is partially filled', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(20, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(4, 18),
|
|
});
|
|
// Match signedOrderLeft with signedOrderRight
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(50, 16), // 50%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(50, 16), // 50%
|
|
};
|
|
// Match signedOrderLeft with signedOrderRight
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
false,
|
|
);
|
|
});
|
|
|
|
it('should transfer the correct amounts when right order is completely filled and left order is partially filled', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(50, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(100, 18),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
});
|
|
// Match signedOrderLeft with signedOrderRight
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 16), // 10%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(10, 16), // 10%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
// Match signedOrderLeft with signedOrderRight
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
false,
|
|
);
|
|
});
|
|
|
|
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 orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(50, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(100, 18),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
});
|
|
// Match orders
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 16), // 10%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(10, 16), // 10%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
// prettier-ignore
|
|
const matchResults = await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
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 orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(100, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(50, 18),
|
|
});
|
|
// Match signedOrderLeft with signedOrderRight2
|
|
const expectedTransferAmounts2 = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(45, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(90, 16), // 90% (10% paid earlier)
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(90, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(90, 16), // 90%
|
|
// Taker
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(90, 16), // 90% (10% paid earlier)
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(90, 16), // 90%
|
|
};
|
|
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight2,
|
|
leftOrderTakerAssetFilledAmount: matchResults.orders.leftOrderTakerAssetFilledAmount,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts2,
|
|
false,
|
|
await matchOrderTester.getBalancesAsync(),
|
|
);
|
|
});
|
|
|
|
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 orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
});
|
|
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(50, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(100, 18),
|
|
});
|
|
// Match orders
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(4, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(4, 16), // 4%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(6, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(4, 16), // 4%
|
|
};
|
|
const matchResults = await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
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 orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(100, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(50, 18),
|
|
});
|
|
// Match signedOrderLeft2 with signedOrderRight
|
|
const expectedTransferAmounts2 = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(96, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(96, 16), // 96%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(48, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(96, 16), // 96%
|
|
// Taker
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(96, 16), // 96%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(96, 16), // 96%
|
|
};
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft2,
|
|
rightOrder: signedOrderRight,
|
|
rightOrderTakerAssetFilledAmount: matchResults.orders.rightOrderTakerAssetFilledAmount,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts2,
|
|
false,
|
|
await matchOrderTester.getBalancesAsync(),
|
|
);
|
|
});
|
|
|
|
it('should transfer the correct amounts if fee recipient is the same across both matched orders', async () => {
|
|
const feeRecipientAddress = feeRecipientAddressLeft;
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
feeRecipientAddress,
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
feeRecipientAddress,
|
|
});
|
|
// Match orders
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
false,
|
|
);
|
|
});
|
|
|
|
it('should transfer the correct amounts if taker == leftMaker', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
});
|
|
// Match orders
|
|
takerAddress = signedOrderLeft.makerAddress;
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
false,
|
|
);
|
|
});
|
|
|
|
it('should transfer the correct amounts if taker == rightMaker', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
});
|
|
// Match orders
|
|
takerAddress = signedOrderRight.makerAddress;
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
false,
|
|
);
|
|
});
|
|
|
|
it('should transfer the correct amounts if taker == leftFeeRecipient', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
});
|
|
// Match orders
|
|
takerAddress = feeRecipientAddressLeft;
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
false,
|
|
);
|
|
});
|
|
|
|
it('should transfer the correct amounts if taker == rightFeeRecipient', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
});
|
|
// Match orders
|
|
takerAddress = feeRecipientAddressRight;
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
false,
|
|
);
|
|
});
|
|
|
|
it('should transfer the correct amounts if leftMaker == leftFeeRecipient && rightMaker == rightFeeRecipient', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
feeRecipientAddress: makerAddressLeft,
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
feeRecipientAddress: makerAddressRight,
|
|
});
|
|
// Match orders
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
false,
|
|
);
|
|
});
|
|
|
|
it('should transfer the correct amounts if leftMaker == leftFeeRecipient && leftMakerFeeAsset == leftTakerAsset', async () => {
|
|
// Create orders to match
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
});
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
makerFeeAssetData: signedOrderRight.makerAssetData,
|
|
feeRecipientAddress: makerAddressLeft,
|
|
});
|
|
// Match orders
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
false,
|
|
);
|
|
});
|
|
|
|
it('should transfer the correct amounts if rightMaker == rightFeeRecipient && rightMakerFeeAsset == rightTakerAsset', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
makerFeeAssetData: signedOrderLeft.makerAssetData,
|
|
feeRecipientAddress: makerAddressRight,
|
|
});
|
|
// Match orders
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
false,
|
|
);
|
|
});
|
|
|
|
it('should transfer the correct amounts if rightMaker == rightFeeRecipient && rightTakerAsset == rightMakerFeeAsset && leftMaker == leftFeeRecipient && leftTakerAsset == leftMakerFeeAsset', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
makerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
feeRecipientAddress: makerAddressLeft,
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
makerFeeAssetData: signedOrderLeft.makerAssetData,
|
|
feeRecipientAddress: makerAddressRight,
|
|
});
|
|
// Match orders
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
false,
|
|
);
|
|
});
|
|
|
|
it('Should revert if left order is not fillable', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
});
|
|
const orderHashHexLeft = orderHashUtils.getOrderHashHex(signedOrderLeft);
|
|
// Cancel left order
|
|
await exchangeWrapper.cancelOrderAsync(signedOrderLeft, signedOrderLeft.makerAddress);
|
|
// Match orders
|
|
const expectedError = new ExchangeRevertErrors.OrderStatusError(orderHashHexLeft, OrderStatus.Cancelled);
|
|
const tx = exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress);
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('Should revert if right order is not fillable', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
});
|
|
const orderHashHexRight = orderHashUtils.getOrderHashHex(signedOrderRight);
|
|
// Cancel right order
|
|
await exchangeWrapper.cancelOrderAsync(signedOrderRight, signedOrderRight.makerAddress);
|
|
// Match orders
|
|
const expectedError = new ExchangeRevertErrors.OrderStatusError(orderHashHexRight, OrderStatus.Cancelled);
|
|
const tx = exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress);
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('should revert if there is not a positive spread', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(100, 18),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(1, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(200, 18),
|
|
});
|
|
const orderHashHexLeft = orderHashUtils.getOrderHashHex(signedOrderLeft);
|
|
const orderHashHexRight = orderHashUtils.getOrderHashHex(signedOrderRight);
|
|
// Match orders
|
|
const expectedError = new ExchangeRevertErrors.NegativeSpreadError(orderHashHexLeft, orderHashHexRight);
|
|
const tx = exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress);
|
|
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 orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.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.BadSignature,
|
|
orderHashHex,
|
|
signedOrderRight.makerAddress,
|
|
signedOrderRight.signature,
|
|
);
|
|
// Match orders
|
|
const tx = exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress);
|
|
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 orderFactoryLeft.newSignedOrderAsync({
|
|
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
});
|
|
const reconstructedOrderRight = {
|
|
...signedOrderRight,
|
|
makerAssetData: signedOrderLeft.takerAssetData,
|
|
};
|
|
const orderHashHex = orderHashUtils.getOrderHashHex(reconstructedOrderRight);
|
|
const expectedError = new ExchangeRevertErrors.SignatureError(
|
|
ExchangeRevertErrors.SignatureErrorCode.BadSignature,
|
|
orderHashHex,
|
|
signedOrderRight.makerAddress,
|
|
signedOrderRight.signature,
|
|
);
|
|
// Match orders
|
|
const tx = exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress);
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
});
|
|
describe('matchOrdersWithMaximalFill', () => {
|
|
it('should transfer correct amounts when right order is fully filled and values pass isRoundingErrorCeil but fail isRoundingErrorFloor', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAddress: makerAddressLeft,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(17, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(98, 0),
|
|
feeRecipientAddress: feeRecipientAddressLeft,
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAddress: makerAddressRight,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(75, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(13, 0),
|
|
feeRecipientAddress: feeRecipientAddressRight,
|
|
});
|
|
// Assert is rounding error ceil & not rounding error floor
|
|
// These assertions are taken from MixinMatchOrders::calculateMatchedFillResults
|
|
// The rounding error is derived computating 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: Web3Wrapper.toBaseUnitAmount(13, 0),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('76.4705882352941176'),
|
|
16,
|
|
), // 76.47%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(75, 0),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('76.5306122448979591'),
|
|
16,
|
|
), // 76.53%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
true,
|
|
);
|
|
});
|
|
|
|
it('Should transfer correct amounts when left order is fully filled and values pass isRoundingErrorCeil and isRoundingErrorFloor', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAddress: makerAddressLeft,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(15, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(90, 0),
|
|
feeRecipientAddress: feeRecipientAddressLeft,
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAddress: makerAddressRight,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(196, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(28, 0),
|
|
feeRecipientAddress: feeRecipientAddressRight,
|
|
});
|
|
// Assert is rounding error floor
|
|
// These assertions are taken from MixinMatchOrders::calculateMatchedFillResults
|
|
// The rounding error is derived computating how much the right maker will buy.
|
|
const numerator = signedOrderRight.makerAssetAmount;
|
|
const denominator = signedOrderRight.takerAssetAmount;
|
|
const target = signedOrderLeft.makerAssetAmount;
|
|
const _isRoundingErrorCeil = isRoundingErrorCeil(numerator, denominator, target);
|
|
expect(_isRoundingErrorCeil).to.be.false();
|
|
const _isRoundingErrorFloor = isRoundingErrorFloor(numerator, denominator, target);
|
|
expect(_isRoundingErrorFloor).to.be.false();
|
|
// Match signedOrderLeft with signedOrderRight
|
|
// Note that the 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: Web3Wrapper.toBaseUnitAmount(15, 0),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightMakerAssetBoughtByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(90, 0),
|
|
// Right Maker
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(15, 0),
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(105, 0),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('53.5714285714285714'),
|
|
16,
|
|
), // 53.57%
|
|
// Taker
|
|
rightMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(15, 0),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('53.5714285714285714'),
|
|
16,
|
|
), // 53.57%
|
|
};
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
true,
|
|
);
|
|
});
|
|
|
|
it('Should transfer correct amounts when left order is fully filled', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAddress: makerAddressLeft,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(16, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(22, 0),
|
|
feeRecipientAddress: feeRecipientAddressLeft,
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAddress: makerAddressRight,
|
|
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(87, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(48, 0),
|
|
feeRecipientAddress: feeRecipientAddressRight,
|
|
});
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(16, 0),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightMakerAssetBoughtByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(22, 0),
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(29, 0),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(16, 0),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('33.3333333333333333'),
|
|
16,
|
|
), // 33.33%
|
|
// Taker
|
|
rightMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(7, 0),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('33.3333333333333333'),
|
|
16,
|
|
), // 33.33%
|
|
};
|
|
// Match signedOrderLeft with signedOrderRight
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
true,
|
|
);
|
|
});
|
|
|
|
it('should fully fill both orders and pay out profit in both maker assets', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAddress: makerAddressLeft,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(7, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(4, 0),
|
|
feeRecipientAddress: feeRecipientAddressLeft,
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAddress: makerAddressRight,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(8, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(6, 0),
|
|
});
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(7, 0),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightMakerAssetBoughtByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(4, 0), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(8, 0),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(6, 0), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
|
rightMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(4, 0),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), //
|
|
};
|
|
// Match signedOrderLeft with signedOrderRight
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
true,
|
|
);
|
|
});
|
|
|
|
it('Should give left maker a better sell price when rounding', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAddress: makerAddressLeft,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(12, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(97, 0),
|
|
feeRecipientAddress: feeRecipientAddressLeft,
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAddress: makerAddressRight,
|
|
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(89, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
|
feeRecipientAddress: feeRecipientAddressRight,
|
|
});
|
|
// Note:
|
|
// The maker/taker fee percentage paid on the left order differs because
|
|
// they received different sale prices. The left maker pays a fee
|
|
// slightly lower than the left taker.
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(11, 0),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('91.6666666666666666'),
|
|
16,
|
|
), // 91.6%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(89, 0),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(10, 0),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('91.7525773195876288'),
|
|
16,
|
|
), // 91.75%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
// Match signedOrderLeft with signedOrderRight
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
true,
|
|
);
|
|
});
|
|
|
|
it('Should give right maker and right taker a favorable fee price when rounding', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAddress: makerAddressLeft,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(16, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(22, 0),
|
|
feeRecipientAddress: feeRecipientAddressLeft,
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAddress: makerAddressRight,
|
|
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(87, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(48, 0),
|
|
feeRecipientAddress: feeRecipientAddressRight,
|
|
makerFee: Web3Wrapper.toBaseUnitAmount(10000, 0),
|
|
takerFee: Web3Wrapper.toBaseUnitAmount(10000, 0),
|
|
});
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(16, 0),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightMakerAssetBoughtByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(22, 0),
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(29, 0),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(16, 0),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(3333, 0), // 3333.3 repeating rounded down to 3333
|
|
// Taker
|
|
rightMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(7, 0),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(3333, 0), // 3333.3 repeating rounded down to 3333
|
|
};
|
|
// Match signedOrderLeft with signedOrderRight
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
true,
|
|
);
|
|
});
|
|
|
|
it('Should give left maker and left taker a favorable fee price when rounding', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAddress: makerAddressLeft,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(12, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(97, 0),
|
|
feeRecipientAddress: feeRecipientAddressLeft,
|
|
makerFee: Web3Wrapper.toBaseUnitAmount(10000, 0),
|
|
takerFee: Web3Wrapper.toBaseUnitAmount(10000, 0),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAddress: makerAddressRight,
|
|
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(89, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
|
feeRecipientAddress: feeRecipientAddressRight,
|
|
});
|
|
// Note:
|
|
// The maker/taker fee percentage paid on the left order differs because
|
|
// they received different sale prices. The left maker pays a
|
|
// fee slightly lower than the left taker.
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(11, 0),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(9166, 0), // 9166.6 rounded down to 9166
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(89, 0),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(10, 0),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(9175, 0), // 9175.2 rounded down to 9175
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
// Match signedOrderLeft with signedOrderRight
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
true,
|
|
);
|
|
});
|
|
|
|
it('Should give left maker a better sell price when rounding', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAddress: makerAddressLeft,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(12, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(97, 0),
|
|
feeRecipientAddress: feeRecipientAddressLeft,
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAddress: makerAddressRight,
|
|
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(89, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
|
feeRecipientAddress: feeRecipientAddressRight,
|
|
});
|
|
// Note:
|
|
// The maker/taker fee percentage paid on the left order differs because
|
|
// they received different sale prices. The left maker pays a fee
|
|
// slightly lower than the left taker.
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(11, 0),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('91.6666666666666666'),
|
|
16,
|
|
), // 91.6%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(89, 0),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(10, 0),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('91.7525773195876288'),
|
|
16,
|
|
), // 91.75%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
// Match signedOrderLeft with signedOrderRight
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
true,
|
|
);
|
|
});
|
|
|
|
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 orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(50, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(100, 18),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
});
|
|
// Match orders
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 16), // 10%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(10, 16), // 10%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
// prettier-ignore
|
|
const matchResults = await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
true,
|
|
);
|
|
// 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 orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(100, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(50, 18),
|
|
});
|
|
// Match signedOrderLeft with signedOrderRight2
|
|
const expectedTransferAmounts2 = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(45, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(90, 16), // 90% (10% paid earlier)
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(90, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(90, 16), // 90%
|
|
// Taker
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(90, 16), // 90% (10% paid earlier)
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(90, 16), // 90%
|
|
};
|
|
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight2,
|
|
leftOrderTakerAssetFilledAmount: matchResults.orders.leftOrderTakerAssetFilledAmount,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts2,
|
|
true,
|
|
await matchOrderTester.getBalancesAsync(),
|
|
);
|
|
});
|
|
|
|
it('Should transfer correct amounts when right order fill amount deviates from amount derived by `Exchange.fillOrder`', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAddress: makerAddressLeft,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(1000, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(1005, 0),
|
|
feeRecipientAddress: feeRecipientAddressLeft,
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAddress: makerAddressRight,
|
|
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(2126, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(1063, 0),
|
|
feeRecipientAddress: feeRecipientAddressRight,
|
|
});
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(1000, 0),
|
|
rightMakerAssetBoughtByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(1005, 0),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.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: Web3Wrapper.toBaseUnitAmount(2000, 0),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('94.0733772342427093'),
|
|
16,
|
|
), // 94.07%
|
|
// Taker
|
|
rightMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(995, 0),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('94.0733772342427093'),
|
|
16,
|
|
), // 94.07%
|
|
};
|
|
// Match signedOrderLeft with signedOrderRight
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
true,
|
|
);
|
|
});
|
|
|
|
it('should transfer the correct amounts when orders completely fill each other and taker doesnt take a profit', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
});
|
|
// Match signedOrderLeft with signedOrderRight
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
// Match signedOrderLeft with signedOrderRight
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
true,
|
|
);
|
|
});
|
|
|
|
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 orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
});
|
|
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(50, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(100, 18),
|
|
});
|
|
|
|
// Match orders
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightMakerAssetBoughtByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 16), // 10%
|
|
// Taker
|
|
rightMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(10, 16), // 10%
|
|
};
|
|
const matchResults = await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
true,
|
|
);
|
|
|
|
// 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 orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(100, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(50, 18),
|
|
});
|
|
|
|
// Match signedOrderLeft2 with signedOrderRight
|
|
const expectedTransferAmounts2 = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(90, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(90, 16), // 90%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(45, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(90, 16), // 90%
|
|
// Taker
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(90, 16), // 96%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(90, 16), // 90%
|
|
};
|
|
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft2,
|
|
rightOrder: signedOrderRight,
|
|
rightOrderTakerAssetFilledAmount: matchResults.orders.rightOrderTakerAssetFilledAmount,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts2,
|
|
true,
|
|
await matchOrderTester.getBalancesAsync(),
|
|
);
|
|
});
|
|
|
|
it('should transfer the correct amounts if fee recipient is the same across both matched orders', async () => {
|
|
const feeRecipientAddress = feeRecipientAddressLeft;
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
feeRecipientAddress,
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
feeRecipientAddress,
|
|
});
|
|
// Match orders
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
true,
|
|
);
|
|
});
|
|
|
|
it('should transfer the correct amounts if taker == leftMaker', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
});
|
|
// Match orders
|
|
takerAddress = signedOrderLeft.makerAddress;
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
true,
|
|
);
|
|
});
|
|
|
|
it('should transfer the correct amounts if taker == rightMaker', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
});
|
|
// Match orders
|
|
takerAddress = signedOrderRight.makerAddress;
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
true,
|
|
);
|
|
});
|
|
|
|
it('should transfer the correct amounts if taker == leftFeeRecipient', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
});
|
|
// Match orders
|
|
takerAddress = feeRecipientAddressLeft;
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
true,
|
|
);
|
|
});
|
|
|
|
it('should transfer the correct amounts if taker == rightFeeRecipient', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
});
|
|
// Match orders
|
|
takerAddress = feeRecipientAddressRight;
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
true,
|
|
);
|
|
});
|
|
|
|
it('should transfer the correct amounts if leftMaker == leftFeeRecipient && rightMaker == rightFeeRecipient', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
feeRecipientAddress: makerAddressLeft,
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
feeRecipientAddress: makerAddressRight,
|
|
});
|
|
// Match orders
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
true,
|
|
);
|
|
});
|
|
|
|
it('should transfer the correct amounts if leftMaker == leftFeeRecipient && leftMakerFeeAsset == leftTakerAsset', async () => {
|
|
// Create orders to match
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
});
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
makerFeeAssetData: signedOrderRight.makerAssetData,
|
|
feeRecipientAddress: makerAddressLeft,
|
|
});
|
|
// Match orders
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
true,
|
|
);
|
|
});
|
|
|
|
it('should transfer the correct amounts if rightMaker == rightFeeRecipient && rightMakerFeeAsset == rightTakerAsset', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
makerFeeAssetData: signedOrderLeft.makerAssetData,
|
|
feeRecipientAddress: makerAddressRight,
|
|
});
|
|
// Match orders
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
true,
|
|
);
|
|
});
|
|
|
|
it('should transfer the correct amounts if rightMaker == rightFeeRecipient && rightTakerAsset == rightMakerFeeAsset && leftMaker == leftFeeRecipient && leftTakerAsset == leftMakerFeeAsset', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
makerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
feeRecipientAddress: makerAddressLeft,
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
makerFeeAssetData: signedOrderLeft.makerAssetData,
|
|
feeRecipientAddress: makerAddressRight,
|
|
});
|
|
// Match orders
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(3, 18),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
};
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
true,
|
|
);
|
|
});
|
|
|
|
it('Should revert if left order is not fillable', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
});
|
|
const orderHashHexLeft = orderHashUtils.getOrderHashHex(signedOrderLeft);
|
|
// Cancel left order
|
|
await exchangeWrapper.cancelOrderAsync(signedOrderLeft, signedOrderLeft.makerAddress);
|
|
// Match orders
|
|
const expectedError = new ExchangeRevertErrors.OrderStatusError(orderHashHexLeft, OrderStatus.Cancelled);
|
|
const tx = exchangeWrapper.matchOrdersWithMaximalFillAsync(signedOrderLeft, signedOrderRight, takerAddress);
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('Should revert if right order is not fillable', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
});
|
|
const orderHashHexRight = orderHashUtils.getOrderHashHex(signedOrderRight);
|
|
// Cancel right order
|
|
await exchangeWrapper.cancelOrderAsync(signedOrderRight, signedOrderRight.makerAddress);
|
|
// Match orders
|
|
const expectedError = new ExchangeRevertErrors.OrderStatusError(orderHashHexRight, OrderStatus.Cancelled);
|
|
const tx = exchangeWrapper.matchOrdersWithMaximalFillAsync(signedOrderLeft, signedOrderRight, takerAddress);
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
|
|
it('should revert if there is not a positive spread', async () => {
|
|
// Create orders to match
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(100, 18),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(1, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(200, 18),
|
|
});
|
|
const orderHashHexLeft = orderHashUtils.getOrderHashHex(signedOrderLeft);
|
|
const orderHashHexRight = orderHashUtils.getOrderHashHex(signedOrderRight);
|
|
// Match orders
|
|
const expectedError = new ExchangeRevertErrors.NegativeSpreadError(orderHashHexLeft, orderHashHexRight);
|
|
const tx = exchangeWrapper.matchOrdersWithMaximalFillAsync(signedOrderLeft, signedOrderRight, takerAddress);
|
|
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 orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.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.BadSignature,
|
|
orderHashHex,
|
|
signedOrderRight.makerAddress,
|
|
signedOrderRight.signature,
|
|
);
|
|
// Match orders
|
|
const tx = exchangeWrapper.matchOrdersWithMaximalFillAsync(signedOrderLeft, signedOrderRight, takerAddress);
|
|
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 orderFactoryLeft.newSignedOrderAsync({
|
|
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
|
|
});
|
|
const reconstructedOrderRight = {
|
|
...signedOrderRight,
|
|
makerAssetData: signedOrderLeft.takerAssetData,
|
|
};
|
|
const orderHashHex = orderHashUtils.getOrderHashHex(reconstructedOrderRight);
|
|
const expectedError = new ExchangeRevertErrors.SignatureError(
|
|
ExchangeRevertErrors.SignatureErrorCode.BadSignature,
|
|
orderHashHex,
|
|
signedOrderRight.makerAddress,
|
|
signedOrderRight.signature,
|
|
);
|
|
// Match orders
|
|
const tx = exchangeWrapper.matchOrdersWithMaximalFillAsync(signedOrderLeft, signedOrderRight, takerAddress);
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
});
|
|
|
|
describe('matchOrders and matchOrdersWithMaximalFill combinations', () => {
|
|
// tslint:disable: enum-naming
|
|
enum AssetType {
|
|
ERC20A = 'ERC20_A',
|
|
ERC20B = 'ERC20_B',
|
|
ERC20C = 'ERC20_C',
|
|
ERC20D = 'ERC20_D',
|
|
ERC721LeftMaker = 'ERC721_LEFT_MAKER',
|
|
ERC721RightMaker = 'ERC721_RIGHT_MAKER',
|
|
ERC721Taker = 'ERC721_TAKER',
|
|
ERC1155FungibleA = 'ERC1155_FUNGIBLE_A',
|
|
ERC1155FungibleB = 'ERC1155_FUNGIBLE_B',
|
|
ERC1155FungibleC = 'ERC1155_FUNGIBLE_C',
|
|
ERC1155FungibleD = 'ERC1155_FUNGIBLE_D',
|
|
ERC1155NonFungibleLeftMaker = 'ERC1155_NON_FUNGIBLE_LEFT_MAKER',
|
|
ERC1155NonFungibleRightMaker = 'ERC1155_NON_FUNGIBLE_RIGHT_MAKER',
|
|
ERC1155NonFungibleTaker = 'ERC1155_NON_FUNGIBLE_TAKER',
|
|
MultiAssetA = 'MULTI_ASSET_A',
|
|
MultiAssetB = 'MULTI_ASSET_B',
|
|
MultiAssetC = 'MULTI_ASSET_C',
|
|
MultiAssetD = 'MULTI_ASSET_D',
|
|
}
|
|
const fungibleTypes = [
|
|
AssetType.ERC20A,
|
|
AssetType.ERC20B,
|
|
AssetType.ERC20C,
|
|
AssetType.ERC20D,
|
|
AssetType.ERC1155FungibleA,
|
|
AssetType.ERC1155FungibleB,
|
|
AssetType.ERC1155FungibleC,
|
|
AssetType.ERC1155FungibleD,
|
|
AssetType.MultiAssetA,
|
|
AssetType.MultiAssetB,
|
|
AssetType.MultiAssetC,
|
|
AssetType.MultiAssetD,
|
|
];
|
|
interface AssetCombination {
|
|
leftMaker: AssetType;
|
|
rightMaker: AssetType;
|
|
leftMakerFee: AssetType;
|
|
rightMakerFee: AssetType;
|
|
leftTakerFee: AssetType;
|
|
rightTakerFee: AssetType;
|
|
description?: string;
|
|
shouldFail?: boolean;
|
|
}
|
|
const assetCombinations: AssetCombination[] = [
|
|
{
|
|
leftMaker: AssetType.ERC20A,
|
|
rightMaker: AssetType.ERC20B,
|
|
leftMakerFee: AssetType.ERC20C,
|
|
rightMakerFee: AssetType.ERC20C,
|
|
leftTakerFee: AssetType.ERC20C,
|
|
rightTakerFee: AssetType.ERC20C,
|
|
},
|
|
{
|
|
leftMaker: AssetType.ERC721LeftMaker,
|
|
rightMaker: AssetType.ERC721RightMaker,
|
|
leftMakerFee: AssetType.ERC20C,
|
|
rightMakerFee: AssetType.ERC20C,
|
|
leftTakerFee: AssetType.ERC20C,
|
|
rightTakerFee: AssetType.ERC20C,
|
|
},
|
|
{
|
|
leftMaker: AssetType.ERC721LeftMaker,
|
|
rightMaker: AssetType.ERC20A,
|
|
leftMakerFee: AssetType.ERC20C,
|
|
rightMakerFee: AssetType.ERC20C,
|
|
leftTakerFee: AssetType.ERC20C,
|
|
rightTakerFee: AssetType.ERC20C,
|
|
},
|
|
{
|
|
leftMaker: AssetType.ERC20A,
|
|
rightMaker: AssetType.ERC721RightMaker,
|
|
leftMakerFee: AssetType.ERC20C,
|
|
rightMakerFee: AssetType.ERC20C,
|
|
leftTakerFee: AssetType.ERC20C,
|
|
rightTakerFee: AssetType.ERC20C,
|
|
},
|
|
{
|
|
leftMaker: AssetType.ERC1155FungibleA,
|
|
rightMaker: AssetType.ERC20A,
|
|
leftMakerFee: AssetType.ERC20C,
|
|
rightMakerFee: AssetType.ERC20C,
|
|
leftTakerFee: AssetType.ERC20C,
|
|
rightTakerFee: AssetType.ERC20C,
|
|
},
|
|
{
|
|
leftMaker: AssetType.ERC20A,
|
|
rightMaker: AssetType.ERC1155FungibleB,
|
|
leftMakerFee: AssetType.ERC20C,
|
|
rightMakerFee: AssetType.ERC20C,
|
|
leftTakerFee: AssetType.ERC20C,
|
|
rightTakerFee: AssetType.ERC20C,
|
|
},
|
|
{
|
|
leftMaker: AssetType.ERC1155FungibleA,
|
|
rightMaker: AssetType.ERC1155FungibleA,
|
|
leftMakerFee: AssetType.ERC20C,
|
|
rightMakerFee: AssetType.ERC20C,
|
|
leftTakerFee: AssetType.ERC20C,
|
|
rightTakerFee: AssetType.ERC20C,
|
|
},
|
|
{
|
|
leftMaker: AssetType.ERC1155NonFungibleLeftMaker,
|
|
rightMaker: AssetType.ERC20A,
|
|
leftMakerFee: AssetType.ERC20C,
|
|
rightMakerFee: AssetType.ERC20C,
|
|
leftTakerFee: AssetType.ERC20C,
|
|
rightTakerFee: AssetType.ERC20C,
|
|
},
|
|
{
|
|
leftMaker: AssetType.ERC20A,
|
|
rightMaker: AssetType.ERC1155NonFungibleRightMaker,
|
|
leftMakerFee: AssetType.ERC20C,
|
|
rightMakerFee: AssetType.ERC20C,
|
|
leftTakerFee: AssetType.ERC20C,
|
|
rightTakerFee: AssetType.ERC20C,
|
|
},
|
|
{
|
|
leftMaker: AssetType.ERC1155NonFungibleLeftMaker,
|
|
rightMaker: AssetType.ERC1155NonFungibleRightMaker,
|
|
leftMakerFee: AssetType.ERC20C,
|
|
rightMakerFee: AssetType.ERC20C,
|
|
leftTakerFee: AssetType.ERC20C,
|
|
rightTakerFee: AssetType.ERC20C,
|
|
},
|
|
{
|
|
leftMaker: AssetType.ERC1155FungibleA,
|
|
rightMaker: AssetType.ERC20A,
|
|
leftMakerFee: AssetType.ERC20C,
|
|
rightMakerFee: AssetType.ERC20C,
|
|
leftTakerFee: AssetType.ERC20C,
|
|
rightTakerFee: AssetType.ERC20C,
|
|
},
|
|
{
|
|
leftMaker: AssetType.ERC20A,
|
|
rightMaker: AssetType.ERC1155FungibleB,
|
|
leftMakerFee: AssetType.ERC20C,
|
|
rightMakerFee: AssetType.ERC20C,
|
|
leftTakerFee: AssetType.ERC20C,
|
|
rightTakerFee: AssetType.ERC20C,
|
|
},
|
|
{
|
|
leftMaker: AssetType.ERC1155FungibleB,
|
|
rightMaker: AssetType.ERC1155FungibleB,
|
|
leftMakerFee: AssetType.ERC20C,
|
|
rightMakerFee: AssetType.ERC20C,
|
|
leftTakerFee: AssetType.ERC20C,
|
|
rightTakerFee: AssetType.ERC20C,
|
|
},
|
|
{
|
|
leftMaker: AssetType.MultiAssetA,
|
|
rightMaker: AssetType.ERC20A,
|
|
leftMakerFee: AssetType.ERC20C,
|
|
rightMakerFee: AssetType.ERC20C,
|
|
leftTakerFee: AssetType.ERC20C,
|
|
rightTakerFee: AssetType.ERC20C,
|
|
},
|
|
{
|
|
leftMaker: AssetType.ERC20A,
|
|
rightMaker: AssetType.MultiAssetB,
|
|
leftMakerFee: AssetType.ERC20C,
|
|
rightMakerFee: AssetType.ERC20C,
|
|
leftTakerFee: AssetType.ERC20C,
|
|
rightTakerFee: AssetType.ERC20C,
|
|
},
|
|
{
|
|
leftMaker: AssetType.MultiAssetA,
|
|
rightMaker: AssetType.MultiAssetB,
|
|
leftMakerFee: AssetType.ERC20C,
|
|
rightMakerFee: AssetType.ERC20C,
|
|
leftTakerFee: AssetType.ERC20C,
|
|
rightTakerFee: AssetType.ERC20C,
|
|
},
|
|
{
|
|
leftMaker: AssetType.MultiAssetA,
|
|
rightMaker: AssetType.ERC1155FungibleA,
|
|
leftMakerFee: AssetType.ERC1155FungibleA,
|
|
rightMakerFee: AssetType.MultiAssetA,
|
|
leftTakerFee: AssetType.ERC20C,
|
|
rightTakerFee: AssetType.ERC20C,
|
|
},
|
|
{
|
|
description: 'Paying maker fees with the same ERC20 tokens being bought.',
|
|
leftMaker: AssetType.ERC20A,
|
|
rightMaker: AssetType.ERC20B,
|
|
leftMakerFee: AssetType.ERC20B,
|
|
rightMakerFee: AssetType.ERC20A,
|
|
leftTakerFee: AssetType.ERC20B,
|
|
rightTakerFee: AssetType.ERC20A,
|
|
},
|
|
{
|
|
description: 'Paying maker fees with the same ERC20 tokens being sold.',
|
|
leftMaker: AssetType.ERC20A,
|
|
rightMaker: AssetType.ERC20B,
|
|
leftMakerFee: AssetType.ERC20A,
|
|
rightMakerFee: AssetType.ERC20B,
|
|
leftTakerFee: AssetType.ERC20A,
|
|
rightTakerFee: AssetType.ERC20B,
|
|
},
|
|
{
|
|
description: 'Using all the same ERC20 asset.',
|
|
leftMaker: AssetType.ERC20A,
|
|
rightMaker: AssetType.ERC20A,
|
|
leftMakerFee: AssetType.ERC20A,
|
|
rightMakerFee: AssetType.ERC20A,
|
|
leftTakerFee: AssetType.ERC20A,
|
|
rightTakerFee: AssetType.ERC20A,
|
|
},
|
|
{
|
|
description: 'Paying fees with the same MAP assets being sold.',
|
|
leftMaker: AssetType.MultiAssetA,
|
|
rightMaker: AssetType.MultiAssetB,
|
|
leftMakerFee: AssetType.MultiAssetA,
|
|
rightMakerFee: AssetType.MultiAssetB,
|
|
leftTakerFee: AssetType.MultiAssetA,
|
|
rightTakerFee: AssetType.MultiAssetB,
|
|
},
|
|
{
|
|
description: 'Paying fees with the same MAP assets being bought.',
|
|
leftMaker: AssetType.MultiAssetA,
|
|
rightMaker: AssetType.MultiAssetB,
|
|
leftMakerFee: AssetType.MultiAssetB,
|
|
rightMakerFee: AssetType.MultiAssetA,
|
|
leftTakerFee: AssetType.MultiAssetB,
|
|
rightTakerFee: AssetType.MultiAssetA,
|
|
},
|
|
{
|
|
description: 'Using all the same MAP assets.',
|
|
leftMaker: AssetType.MultiAssetA,
|
|
rightMaker: AssetType.MultiAssetA,
|
|
leftMakerFee: AssetType.MultiAssetA,
|
|
rightMakerFee: AssetType.MultiAssetA,
|
|
leftTakerFee: AssetType.MultiAssetA,
|
|
rightTakerFee: AssetType.MultiAssetA,
|
|
},
|
|
{
|
|
description: 'Swapping ERC721s then using them to pay maker fees.',
|
|
leftMaker: AssetType.ERC721LeftMaker,
|
|
rightMaker: AssetType.ERC721RightMaker,
|
|
leftMakerFee: AssetType.ERC721RightMaker,
|
|
rightMakerFee: AssetType.ERC721LeftMaker,
|
|
leftTakerFee: AssetType.ERC20A,
|
|
rightTakerFee: AssetType.ERC20A,
|
|
},
|
|
{
|
|
description: 'Swapping ERC1155 NFTs then using them to pay maker fees.',
|
|
leftMaker: AssetType.ERC1155NonFungibleLeftMaker,
|
|
rightMaker: AssetType.ERC1155NonFungibleRightMaker,
|
|
leftMakerFee: AssetType.ERC1155NonFungibleRightMaker,
|
|
rightMakerFee: AssetType.ERC1155NonFungibleLeftMaker,
|
|
leftTakerFee: AssetType.ERC20A,
|
|
rightTakerFee: AssetType.ERC20A,
|
|
},
|
|
{
|
|
description: 'Double-spend by trying to pay maker fees with sold ERC721 token (fail).',
|
|
leftMaker: AssetType.ERC721LeftMaker,
|
|
rightMaker: AssetType.ERC721RightMaker,
|
|
leftMakerFee: AssetType.ERC721LeftMaker,
|
|
rightMakerFee: AssetType.ERC721LeftMaker,
|
|
leftTakerFee: AssetType.ERC20A,
|
|
rightTakerFee: AssetType.ERC20A,
|
|
shouldFail: true,
|
|
},
|
|
{
|
|
description: 'Double-spend by trying to pay maker fees with sold ERC1155 NFT (fail).',
|
|
leftMaker: AssetType.ERC20A,
|
|
rightMaker: AssetType.ERC1155NonFungibleLeftMaker,
|
|
leftMakerFee: AssetType.ERC20C,
|
|
rightMakerFee: AssetType.ERC1155NonFungibleLeftMaker,
|
|
leftTakerFee: AssetType.ERC20C,
|
|
rightTakerFee: AssetType.ERC20C,
|
|
shouldFail: true,
|
|
},
|
|
];
|
|
|
|
let nameToERC20Asset: { [name: string]: string };
|
|
let nameToERC721Asset: { [name: string]: [string, BigNumber] };
|
|
let nameToERC1155FungibleAsset: { [name: string]: [string, BigNumber] };
|
|
let nameToERC1155NonFungibleAsset: { [name: string]: [string, BigNumber] };
|
|
let nameToMultiAssetAsset: { [name: string]: [BigNumber[], string[]] };
|
|
|
|
function getAssetData(assetType: AssetType): string {
|
|
const encodeERC20AssetData = assetDataUtils.encodeERC20AssetData;
|
|
const encodeERC721AssetData = assetDataUtils.encodeERC721AssetData;
|
|
const encodeERC1155AssetData = assetDataUtils.encodeERC1155AssetData;
|
|
const encodeMultiAssetData = assetDataUtils.encodeMultiAssetData;
|
|
if (nameToERC20Asset[assetType] !== undefined) {
|
|
const tokenAddress = nameToERC20Asset[assetType];
|
|
return encodeERC20AssetData(tokenAddress);
|
|
}
|
|
if (nameToERC721Asset[assetType] !== undefined) {
|
|
const [tokenAddress, tokenId] = nameToERC721Asset[assetType];
|
|
return encodeERC721AssetData(tokenAddress, tokenId);
|
|
}
|
|
if (nameToERC1155FungibleAsset[assetType] !== undefined) {
|
|
const [tokenAddress, tokenId] = nameToERC1155FungibleAsset[assetType];
|
|
return encodeERC1155AssetData(tokenAddress, [tokenId], [ONE], constants.NULL_BYTES);
|
|
}
|
|
if (nameToERC1155NonFungibleAsset[assetType] !== undefined) {
|
|
const [tokenAddress, tokenId] = nameToERC1155NonFungibleAsset[assetType];
|
|
return encodeERC1155AssetData(tokenAddress, [tokenId], [ONE], constants.NULL_BYTES);
|
|
}
|
|
if (nameToMultiAssetAsset[assetType] !== undefined) {
|
|
const [amounts, nestedAssetData] = nameToMultiAssetAsset[assetType];
|
|
return encodeMultiAssetData(amounts, nestedAssetData);
|
|
}
|
|
throw new Error(`Unknown asset type: ${assetType}`);
|
|
}
|
|
|
|
before(async () => {
|
|
nameToERC20Asset = {
|
|
ERC20_A: erc20Tokens[0].address,
|
|
ERC20_B: erc20Tokens[1].address,
|
|
ERC20_C: erc20Tokens[2].address,
|
|
ERC20_D: erc20Tokens[3].address,
|
|
};
|
|
const erc721TokenIds = _.mapValues(tokenBalances.erc721, v => v[defaultERC721AssetAddress][0]);
|
|
nameToERC721Asset = {
|
|
ERC721_LEFT_MAKER: [defaultERC721AssetAddress, erc721TokenIds[makerAddressLeft]],
|
|
ERC721_RIGHT_MAKER: [defaultERC721AssetAddress, erc721TokenIds[makerAddressRight]],
|
|
ERC721_TAKER: [defaultERC721AssetAddress, erc721TokenIds[takerAddress]],
|
|
};
|
|
const erc1155FungibleTokens = _.keys(
|
|
_.values(tokenBalances.erc1155)[0][defaultERC1155AssetAddress].fungible,
|
|
).map(k => new BigNumber(k));
|
|
nameToERC1155FungibleAsset = {
|
|
ERC1155_FUNGIBLE_A: [defaultERC1155AssetAddress, erc1155FungibleTokens[0]],
|
|
ERC1155_FUNGIBLE_B: [defaultERC1155AssetAddress, erc1155FungibleTokens[1]],
|
|
ERC1155_FUNGIBLE_C: [defaultERC1155AssetAddress, erc1155FungibleTokens[2]],
|
|
ERC1155_FUNGIBLE_D: [defaultERC1155AssetAddress, erc1155FungibleTokens[3]],
|
|
};
|
|
const erc1155NonFungibleTokenIds = _.mapValues(
|
|
tokenBalances.erc1155,
|
|
v => v[defaultERC1155AssetAddress].nonFungible[0],
|
|
);
|
|
nameToERC1155NonFungibleAsset = {
|
|
ERC1155_NON_FUNGIBLE_LEFT_MAKER: [
|
|
defaultERC1155AssetAddress,
|
|
erc1155NonFungibleTokenIds[makerAddressLeft],
|
|
],
|
|
ERC1155_NON_FUNGIBLE_RIGHT_MAKER: [
|
|
defaultERC1155AssetAddress,
|
|
erc1155NonFungibleTokenIds[makerAddressRight],
|
|
],
|
|
ERC1155_NON_FUNGIBLE_TAKER: [defaultERC1155AssetAddress, erc1155NonFungibleTokenIds[takerAddress]],
|
|
};
|
|
nameToMultiAssetAsset = {
|
|
MULTI_ASSET_A: [
|
|
[ONE, TWO],
|
|
[
|
|
assetDataUtils.encodeERC20AssetData(erc20Tokens[0].address),
|
|
assetDataUtils.encodeERC1155AssetData(
|
|
defaultERC1155AssetAddress,
|
|
[erc1155FungibleTokens[0]],
|
|
[ONE],
|
|
constants.NULL_BYTES,
|
|
),
|
|
],
|
|
],
|
|
MULTI_ASSET_B: [
|
|
[ONE, TWO],
|
|
[
|
|
assetDataUtils.encodeERC20AssetData(erc20Tokens[1].address),
|
|
assetDataUtils.encodeERC1155AssetData(
|
|
defaultERC1155AssetAddress,
|
|
[erc1155FungibleTokens[1]],
|
|
[ONE],
|
|
constants.NULL_BYTES,
|
|
),
|
|
],
|
|
],
|
|
MULTI_ASSET_C: [
|
|
[ONE, TWO],
|
|
[
|
|
assetDataUtils.encodeERC20AssetData(erc20Tokens[2].address),
|
|
assetDataUtils.encodeERC1155AssetData(
|
|
defaultERC1155AssetAddress,
|
|
[erc1155FungibleTokens[2]],
|
|
[ONE],
|
|
constants.NULL_BYTES,
|
|
),
|
|
],
|
|
],
|
|
MULTI_ASSET_D: [
|
|
[ONE, TWO],
|
|
[
|
|
assetDataUtils.encodeERC20AssetData(erc20Tokens[3].address),
|
|
assetDataUtils.encodeERC1155AssetData(
|
|
erc1155Token.address,
|
|
[erc1155FungibleTokens[3]],
|
|
[ONE],
|
|
constants.NULL_BYTES,
|
|
),
|
|
],
|
|
],
|
|
};
|
|
});
|
|
|
|
// matchOrders
|
|
for (const combo of assetCombinations) {
|
|
const description = combo.description || JSON.stringify(combo);
|
|
it(description, async () => {
|
|
// Create orders to match. For ERC20s, there will be a spread.
|
|
const leftMakerAssetAmount = _.includes(fungibleTypes, combo.leftMaker)
|
|
? Web3Wrapper.toBaseUnitAmount(15, 18)
|
|
: Web3Wrapper.toBaseUnitAmount(1, 0);
|
|
const leftTakerAssetAmount = _.includes(fungibleTypes, combo.rightMaker)
|
|
? Web3Wrapper.toBaseUnitAmount(30, 18)
|
|
: Web3Wrapper.toBaseUnitAmount(1, 0);
|
|
const rightMakerAssetAmount = _.includes(fungibleTypes, combo.rightMaker)
|
|
? Web3Wrapper.toBaseUnitAmount(30, 18)
|
|
: Web3Wrapper.toBaseUnitAmount(1, 0);
|
|
const rightTakerAssetAmount = _.includes(fungibleTypes, combo.leftMaker)
|
|
? Web3Wrapper.toBaseUnitAmount(14, 18)
|
|
: Web3Wrapper.toBaseUnitAmount(1, 0);
|
|
const leftMakerFeeAssetAmount = _.includes(fungibleTypes, combo.leftMakerFee)
|
|
? Web3Wrapper.toBaseUnitAmount(8, 12)
|
|
: Web3Wrapper.toBaseUnitAmount(1, 0);
|
|
const rightMakerFeeAssetAmount = _.includes(fungibleTypes, combo.rightMakerFee)
|
|
? Web3Wrapper.toBaseUnitAmount(7, 12)
|
|
: Web3Wrapper.toBaseUnitAmount(1, 0);
|
|
const leftTakerFeeAssetAmount = _.includes(fungibleTypes, combo.leftTakerFee)
|
|
? Web3Wrapper.toBaseUnitAmount(6, 12)
|
|
: Web3Wrapper.toBaseUnitAmount(1, 0);
|
|
const rightTakerFeeAssetAmount = _.includes(fungibleTypes, combo.rightTakerFee)
|
|
? Web3Wrapper.toBaseUnitAmount(5, 12)
|
|
: Web3Wrapper.toBaseUnitAmount(1, 0);
|
|
const leftMakerAssetReceivedByTakerAmount = _.includes(fungibleTypes, combo.leftMaker)
|
|
? leftMakerAssetAmount.minus(rightTakerAssetAmount)
|
|
: Web3Wrapper.toBaseUnitAmount(0, 0);
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetData: getAssetData(combo.leftMaker),
|
|
takerAssetData: getAssetData(combo.rightMaker),
|
|
makerFeeAssetData: getAssetData(combo.leftMakerFee),
|
|
takerFeeAssetData: getAssetData(combo.leftTakerFee),
|
|
makerAssetAmount: leftMakerAssetAmount,
|
|
takerAssetAmount: leftTakerAssetAmount,
|
|
makerFee: leftMakerFeeAssetAmount,
|
|
takerFee: leftTakerFeeAssetAmount,
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetData: getAssetData(combo.rightMaker),
|
|
takerAssetData: getAssetData(combo.leftMaker),
|
|
makerFeeAssetData: getAssetData(combo.rightMakerFee),
|
|
takerFeeAssetData: getAssetData(combo.rightTakerFee),
|
|
makerAssetAmount: rightMakerAssetAmount,
|
|
takerAssetAmount: rightTakerAssetAmount,
|
|
makerFee: rightMakerFeeAssetAmount,
|
|
takerFee: rightTakerFeeAssetAmount,
|
|
});
|
|
// Match signedOrderLeft with signedOrderRight
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: leftMakerAssetAmount,
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: leftMakerFeeAssetAmount,
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: rightMakerAssetAmount,
|
|
leftMakerAssetBoughtByRightMakerAmount: rightTakerAssetAmount,
|
|
rightMakerFeeAssetPaidByRightMakerAmount: rightMakerFeeAssetAmount,
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount,
|
|
leftTakerFeeAssetPaidByTakerAmount: leftTakerFeeAssetAmount,
|
|
rightTakerFeeAssetPaidByTakerAmount: rightTakerFeeAssetAmount,
|
|
};
|
|
if (!combo.shouldFail) {
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
false,
|
|
);
|
|
} else {
|
|
const tx = exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress);
|
|
return expect(tx).to.be.rejected();
|
|
}
|
|
});
|
|
}
|
|
|
|
// matchOrdersWithMaximalFill
|
|
for (const combo of assetCombinations) {
|
|
const description = combo.description || JSON.stringify(combo);
|
|
it(description, async () => {
|
|
// Create orders to match. For ERC20s, there will be a spread.
|
|
const leftMakerAssetAmount = _.includes(fungibleTypes, combo.leftMaker)
|
|
? Web3Wrapper.toBaseUnitAmount(15, 18)
|
|
: Web3Wrapper.toBaseUnitAmount(1, 0);
|
|
const leftTakerAssetAmount = _.includes(fungibleTypes, combo.rightMaker)
|
|
? Web3Wrapper.toBaseUnitAmount(30, 18)
|
|
: Web3Wrapper.toBaseUnitAmount(1, 0);
|
|
const rightMakerAssetAmount = _.includes(fungibleTypes, combo.rightMaker)
|
|
? Web3Wrapper.toBaseUnitAmount(30, 18)
|
|
: Web3Wrapper.toBaseUnitAmount(1, 0);
|
|
const rightTakerAssetAmount = _.includes(fungibleTypes, combo.leftMaker)
|
|
? Web3Wrapper.toBaseUnitAmount(14, 18)
|
|
: Web3Wrapper.toBaseUnitAmount(1, 0);
|
|
const leftMakerFeeAssetAmount = _.includes(fungibleTypes, combo.leftMakerFee)
|
|
? Web3Wrapper.toBaseUnitAmount(8, 12)
|
|
: Web3Wrapper.toBaseUnitAmount(1, 0);
|
|
const rightMakerFeeAssetAmount = _.includes(fungibleTypes, combo.rightMakerFee)
|
|
? Web3Wrapper.toBaseUnitAmount(7, 12)
|
|
: Web3Wrapper.toBaseUnitAmount(1, 0);
|
|
const leftTakerFeeAssetAmount = _.includes(fungibleTypes, combo.leftTakerFee)
|
|
? Web3Wrapper.toBaseUnitAmount(6, 12)
|
|
: Web3Wrapper.toBaseUnitAmount(1, 0);
|
|
const rightTakerFeeAssetAmount = _.includes(fungibleTypes, combo.rightTakerFee)
|
|
? Web3Wrapper.toBaseUnitAmount(5, 12)
|
|
: Web3Wrapper.toBaseUnitAmount(1, 0);
|
|
const leftMakerAssetReceivedByTakerAmount = _.includes(fungibleTypes, combo.leftMaker)
|
|
? leftMakerAssetAmount.minus(rightTakerAssetAmount)
|
|
: Web3Wrapper.toBaseUnitAmount(0, 0);
|
|
const rightMakerAssetReceivedByTakerAmount = _.includes(fungibleTypes, combo.leftMaker)
|
|
? rightMakerAssetAmount.minus(leftTakerAssetAmount)
|
|
: Web3Wrapper.toBaseUnitAmount(0, 0);
|
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAssetData: getAssetData(combo.leftMaker),
|
|
takerAssetData: getAssetData(combo.rightMaker),
|
|
makerFeeAssetData: getAssetData(combo.leftMakerFee),
|
|
takerFeeAssetData: getAssetData(combo.leftTakerFee),
|
|
makerAssetAmount: leftMakerAssetAmount,
|
|
takerAssetAmount: leftTakerAssetAmount,
|
|
makerFee: leftMakerFeeAssetAmount,
|
|
takerFee: leftTakerFeeAssetAmount,
|
|
});
|
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
makerAssetData: getAssetData(combo.rightMaker),
|
|
takerAssetData: getAssetData(combo.leftMaker),
|
|
makerFeeAssetData: getAssetData(combo.rightMakerFee),
|
|
takerFeeAssetData: getAssetData(combo.rightTakerFee),
|
|
makerAssetAmount: rightMakerAssetAmount,
|
|
takerAssetAmount: rightTakerAssetAmount,
|
|
makerFee: rightMakerFeeAssetAmount,
|
|
takerFee: rightTakerFeeAssetAmount,
|
|
});
|
|
// Match signedOrderLeft with signedOrderRight
|
|
const expectedTransferAmounts = {
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: leftMakerAssetAmount,
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: leftMakerFeeAssetAmount,
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: rightMakerAssetAmount,
|
|
leftMakerAssetBoughtByRightMakerAmount: rightTakerAssetAmount,
|
|
rightMakerFeeAssetPaidByRightMakerAmount: rightMakerFeeAssetAmount,
|
|
// Taker
|
|
leftMakerAssetReceivedByTakerAmount,
|
|
rightMakerAssetReceivedByTakerAmount,
|
|
leftTakerFeeAssetPaidByTakerAmount: leftTakerFeeAssetAmount,
|
|
rightTakerFeeAssetPaidByTakerAmount: rightTakerFeeAssetAmount,
|
|
};
|
|
if (!combo.shouldFail) {
|
|
await matchOrderTester.matchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrder: signedOrderLeft,
|
|
rightOrder: signedOrderRight,
|
|
},
|
|
takerAddress,
|
|
expectedTransferAmounts,
|
|
true,
|
|
);
|
|
} else {
|
|
const tx = exchangeWrapper.matchOrdersWithMaximalFillAsync(
|
|
signedOrderLeft,
|
|
signedOrderRight,
|
|
takerAddress,
|
|
);
|
|
return expect(tx).to.be.rejected();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
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 orderFactoryRight.newSignedOrderAsync({
|
|
makerAddress: makerAddressRight,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 0),
|
|
feeRecipientAddress: feeRecipientAddressRight,
|
|
}),
|
|
];
|
|
const expectedError = new ExchangeRevertErrors.BatchMatchOrdersError(
|
|
ExchangeRevertErrors.BatchMatchOrdersErrorCodes.ZeroLeftOrders,
|
|
);
|
|
let tx = exchangeWrapper.batchMatchOrdersAsync(leftOrders, rightOrders, takerAddress);
|
|
await expect(tx).to.revertWith(expectedError);
|
|
tx = exchangeWrapper.batchMatchOrdersWithMaximalFillAsync(leftOrders, rightOrders, takerAddress);
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
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 expectedError = new ExchangeRevertErrors.BatchMatchOrdersError(
|
|
ExchangeRevertErrors.BatchMatchOrdersErrorCodes.ZeroRightOrders,
|
|
);
|
|
let tx = exchangeWrapper.batchMatchOrdersAsync(leftOrders, rightOrders, takerAddress);
|
|
await expect(tx).to.revertWith(expectedError);
|
|
tx = exchangeWrapper.batchMatchOrdersWithMaximalFillAsync(leftOrders, rightOrders, takerAddress);
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
it('should fail if there are a different number of left orders and signatures', async () => {
|
|
const leftOrders = [
|
|
await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAddress: makerAddressLeft,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
|
feeRecipientAddress: feeRecipientAddressLeft,
|
|
}),
|
|
await orderFactoryRight.newSignedOrderAsync({
|
|
makerAddress: makerAddressRight,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 0),
|
|
feeRecipientAddress: feeRecipientAddressRight,
|
|
}),
|
|
];
|
|
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 params = orderUtils.createBatchMatchOrders(leftOrders, rightOrders);
|
|
// Set params left signatures to only include the first left signature
|
|
params.leftSignatures = [params.leftSignatures[0]];
|
|
const expectedError = new ExchangeRevertErrors.BatchMatchOrdersError(
|
|
ExchangeRevertErrors.BatchMatchOrdersErrorCodes.InvalidLengthLeftSignatures,
|
|
);
|
|
let tx = exchangeWrapper.batchMatchOrdersRawAsync(params, takerAddress);
|
|
await expect(tx).to.revertWith(expectedError);
|
|
tx = exchangeWrapper.batchMatchOrdersWithMaximalFillRawAsync(params, takerAddress);
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
it('should fail if there are a different number of right orders and signatures', async () => {
|
|
const leftOrders = [
|
|
await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAddress: makerAddressLeft,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
|
feeRecipientAddress: feeRecipientAddressLeft,
|
|
}),
|
|
await orderFactoryRight.newSignedOrderAsync({
|
|
makerAddress: makerAddressRight,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 0),
|
|
feeRecipientAddress: feeRecipientAddressRight,
|
|
}),
|
|
];
|
|
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 params = orderUtils.createBatchMatchOrders(leftOrders, rightOrders);
|
|
// Set params right signatures to only include the first right signature
|
|
params.rightSignatures = [params.rightSignatures[0]];
|
|
const expectedError = new ExchangeRevertErrors.BatchMatchOrdersError(
|
|
ExchangeRevertErrors.BatchMatchOrdersErrorCodes.InvalidLengthRightSignatures,
|
|
);
|
|
let tx = exchangeWrapper.batchMatchOrdersRawAsync(params, takerAddress);
|
|
await expect(tx).to.revertWith(expectedError);
|
|
tx = exchangeWrapper.batchMatchOrdersWithMaximalFillRawAsync(params, takerAddress);
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
});
|
|
describe('batchMatchOrders', () => {
|
|
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,
|
|
false,
|
|
);
|
|
});
|
|
it('Should correctly match a partial fill', 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,
|
|
}),
|
|
];
|
|
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%
|
|
},
|
|
];
|
|
await matchOrderTester.batchMatchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrders,
|
|
rightOrders,
|
|
leftOrdersTakerAssetFilledAmounts: [ZERO],
|
|
rightOrdersTakerAssetFilledAmounts: [ZERO],
|
|
},
|
|
takerAddress,
|
|
[[0, 0]],
|
|
expectedTransferAmounts,
|
|
false,
|
|
);
|
|
});
|
|
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
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 0),
|
|
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
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(2, 0),
|
|
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,
|
|
false,
|
|
);
|
|
});
|
|
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,
|
|
false,
|
|
);
|
|
});
|
|
it('should correctly match one left order to two right orders, where the last should not be touched', 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,
|
|
}),
|
|
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, ZERO],
|
|
},
|
|
takerAddress,
|
|
[[0, 0]],
|
|
expectedTransferAmounts,
|
|
false,
|
|
);
|
|
});
|
|
it('should have three order matchings with only two left orders and two right orders', async () => {
|
|
const leftOrders = [
|
|
await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAddress: makerAddressLeft,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(4, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 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(1, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 0),
|
|
feeRecipientAddress: feeRecipientAddressRight,
|
|
}),
|
|
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(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(50, 16), // 50%
|
|
// Taker
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(50, 16), // 50%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(50, 16), // 50%
|
|
},
|
|
{
|
|
// 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), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(50, 16), // 50%
|
|
},
|
|
];
|
|
await matchOrderTester.batchMatchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrders,
|
|
rightOrders,
|
|
leftOrdersTakerAssetFilledAmounts: [ZERO, ZERO],
|
|
rightOrdersTakerAssetFilledAmounts: [ZERO, ZERO],
|
|
},
|
|
takerAddress,
|
|
[[0, 0], [0, 1], [1, 1]],
|
|
expectedTransferAmounts,
|
|
false,
|
|
);
|
|
});
|
|
});
|
|
describe('batchMatchOrdersWithMaximalFill', () => {
|
|
it('should fully fill the the right order and pay the profit denominated in the left maker asset', async () => {
|
|
// Create orders to match
|
|
const leftOrders = [
|
|
await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAddress: makerAddressLeft,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(17, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(98, 0),
|
|
feeRecipientAddress: feeRecipientAddressLeft,
|
|
}),
|
|
];
|
|
const rightOrders = [
|
|
await orderFactoryRight.newSignedOrderAsync({
|
|
makerAddress: makerAddressRight,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(75, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(13, 0),
|
|
feeRecipientAddress: feeRecipientAddressRight,
|
|
}),
|
|
];
|
|
const expectedTransferAmounts = [
|
|
{
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(13, 0),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('76.4705882352941176'),
|
|
16,
|
|
), // 76.47%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(75, 0),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('76.5306122448979591'),
|
|
16,
|
|
), // 76.53%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
},
|
|
];
|
|
await matchOrderTester.batchMatchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrders,
|
|
rightOrders,
|
|
leftOrdersTakerAssetFilledAmounts: [ZERO],
|
|
rightOrdersTakerAssetFilledAmounts: [ZERO],
|
|
},
|
|
takerAddress,
|
|
[[0, 0]],
|
|
expectedTransferAmounts,
|
|
true,
|
|
);
|
|
});
|
|
it('Should transfer correct amounts when left order is fully filled', async () => {
|
|
// Create orders to match
|
|
const leftOrders = [
|
|
await orderFactoryLeft.newSignedOrderAsync({
|
|
makerAddress: makerAddressLeft,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(15, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(90, 0),
|
|
feeRecipientAddress: feeRecipientAddressLeft,
|
|
}),
|
|
];
|
|
const rightOrders = [
|
|
await orderFactoryRight.newSignedOrderAsync({
|
|
makerAddress: makerAddressRight,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(196, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(28, 0),
|
|
feeRecipientAddress: feeRecipientAddressRight,
|
|
}),
|
|
];
|
|
// Match signedOrderLeft with signedOrderRight
|
|
// Note that the 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: Web3Wrapper.toBaseUnitAmount(15, 0),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightMakerAssetBoughtByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(90, 0),
|
|
// Right Maker
|
|
leftMakerAssetBoughtByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(15, 0),
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(105, 0),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('53.5714285714285714'),
|
|
16,
|
|
), // 53.57%
|
|
// Taker
|
|
rightMakerAssetReceivedByTakerAmount: Web3Wrapper.toBaseUnitAmount(15, 0),
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('53.5714285714285714'),
|
|
16,
|
|
), // 53.57%
|
|
},
|
|
];
|
|
await matchOrderTester.batchMatchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrders,
|
|
rightOrders,
|
|
leftOrdersTakerAssetFilledAmounts: [ZERO],
|
|
rightOrdersTakerAssetFilledAmounts: [ZERO],
|
|
},
|
|
takerAddress,
|
|
[[0, 0]],
|
|
expectedTransferAmounts,
|
|
true,
|
|
);
|
|
});
|
|
it('should correctly match one left order to two right orders, where the last should not be touched', 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,
|
|
}),
|
|
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, ZERO],
|
|
},
|
|
takerAddress,
|
|
[[0, 0]],
|
|
expectedTransferAmounts,
|
|
true,
|
|
);
|
|
});
|
|
it('should correctly fill all four orders in three matches', 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(72, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(36, 0),
|
|
feeRecipientAddress: feeRecipientAddressLeft,
|
|
}),
|
|
];
|
|
const rightOrders = [
|
|
await orderFactoryRight.newSignedOrderAsync({
|
|
makerAddress: makerAddressRight,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(15, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(30, 0),
|
|
feeRecipientAddress: feeRecipientAddressRight,
|
|
}),
|
|
await orderFactoryRight.newSignedOrderAsync({
|
|
makerAddress: makerAddressRight,
|
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(22, 0),
|
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(44, 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(
|
|
new BigNumber('6.6666666666666666'),
|
|
16,
|
|
), // 6.66%
|
|
// Taker
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('6.6666666666666666'),
|
|
16,
|
|
), // 6.66%
|
|
},
|
|
{
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(28, 0),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('38.8888888888888888'),
|
|
16,
|
|
), // 38.88%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(14, 0),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('93.3333333333333333'),
|
|
16,
|
|
), // 93.33%
|
|
// Taker
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('38.8888888888888888'),
|
|
16,
|
|
), // 38.88%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('93.3333333333333333'),
|
|
16,
|
|
), // 93.33%
|
|
},
|
|
{
|
|
// Left Maker
|
|
leftMakerAssetSoldByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(44, 0),
|
|
leftMakerFeeAssetPaidByLeftMakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('61.1111111111111111'),
|
|
16,
|
|
), // 61.11%
|
|
// Right Maker
|
|
rightMakerAssetSoldByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(22, 0),
|
|
rightMakerFeeAssetPaidByRightMakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
// Taker
|
|
leftTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(
|
|
new BigNumber('61.1111111111111111'),
|
|
16,
|
|
), // 61.11%
|
|
rightTakerFeeAssetPaidByTakerAmount: Web3Wrapper.toBaseUnitAmount(100, 16), // 100%
|
|
},
|
|
];
|
|
await matchOrderTester.batchMatchOrdersAndAssertEffectsAsync(
|
|
{
|
|
leftOrders,
|
|
rightOrders,
|
|
leftOrdersTakerAssetFilledAmounts: [ZERO, ZERO],
|
|
rightOrdersTakerAssetFilledAmounts: [ZERO, ZERO],
|
|
},
|
|
takerAddress,
|
|
[[0, 0], [1, 0], [1, 1]],
|
|
expectedTransferAmounts,
|
|
true,
|
|
);
|
|
});
|
|
});
|
|
});
|
|
// tslint:disable-line:max-file-line-count
|