Merge pull request #572 from 0xProject/feature/contracts/atomicMatching
Atomic Order Matching
This commit is contained in:
@@ -12,7 +12,7 @@ import { ERC721ProxyContract } from '../../src/contract_wrappers/generated/e_r_c
|
||||
import {
|
||||
CancelContractEventArgs,
|
||||
ExchangeContract,
|
||||
ExchangeErrorContractEventArgs,
|
||||
ExchangeStatusContractEventArgs,
|
||||
FillContractEventArgs,
|
||||
} from '../../src/contract_wrappers/generated/exchange';
|
||||
import { artifacts } from '../../src/utils/artifacts';
|
||||
@@ -25,7 +25,7 @@ import { ERC721Wrapper } from '../../src/utils/erc721_wrapper';
|
||||
import { ExchangeWrapper } from '../../src/utils/exchange_wrapper';
|
||||
import { OrderFactory } from '../../src/utils/order_factory';
|
||||
import { orderUtils } from '../../src/utils/order_utils';
|
||||
import { AssetProxyId, ERC20BalancesByOwner, ExchangeContractErrs, SignedOrder } from '../../src/utils/types';
|
||||
import { AssetProxyId, ContractName, ERC20BalancesByOwner, ExchangeStatus, SignedOrder } from '../../src/utils/types';
|
||||
import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper';
|
||||
|
||||
chaiSetup.configure();
|
||||
@@ -556,9 +556,9 @@ describe('Exchange core', () => {
|
||||
|
||||
const res = await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
|
||||
expect(res.logs).to.have.length(1);
|
||||
const log = res.logs[0] as LogWithDecodedArgs<ExchangeErrorContractEventArgs>;
|
||||
const errCode = log.args.errorId;
|
||||
expect(errCode).to.be.equal(ExchangeContractErrs.ERROR_ORDER_EXPIRED);
|
||||
const log = res.logs[0] as LogWithDecodedArgs<ExchangeStatusContractEventArgs>;
|
||||
const statusCode = log.args.statusId;
|
||||
expect(statusCode).to.be.equal(ExchangeStatus.ORDER_EXPIRED);
|
||||
});
|
||||
|
||||
it('should log an error event if no value is filled', async () => {
|
||||
@@ -567,9 +567,9 @@ describe('Exchange core', () => {
|
||||
|
||||
const res = await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
|
||||
expect(res.logs).to.have.length(1);
|
||||
const log = res.logs[0] as LogWithDecodedArgs<ExchangeErrorContractEventArgs>;
|
||||
const errCode = log.args.errorId;
|
||||
expect(errCode).to.be.equal(ExchangeContractErrs.ERROR_ORDER_FULLY_FILLED);
|
||||
const log = res.logs[0] as LogWithDecodedArgs<ExchangeStatusContractEventArgs>;
|
||||
const statusCode = log.args.statusId;
|
||||
expect(statusCode).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -635,9 +635,9 @@ describe('Exchange core', () => {
|
||||
|
||||
const res = await exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress);
|
||||
expect(res.logs).to.have.length(1);
|
||||
const log = res.logs[0] as LogWithDecodedArgs<ExchangeErrorContractEventArgs>;
|
||||
const errCode = log.args.errorId;
|
||||
expect(errCode).to.be.equal(ExchangeContractErrs.ERROR_ORDER_CANCELLED);
|
||||
const log = res.logs[0] as LogWithDecodedArgs<ExchangeStatusContractEventArgs>;
|
||||
const statusCode = log.args.statusId;
|
||||
expect(statusCode).to.be.equal(ExchangeStatus.ORDER_CANCELLED);
|
||||
});
|
||||
|
||||
it('should log error if order is expired', async () => {
|
||||
@@ -647,9 +647,9 @@ describe('Exchange core', () => {
|
||||
|
||||
const res = await exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress);
|
||||
expect(res.logs).to.have.length(1);
|
||||
const log = res.logs[0] as LogWithDecodedArgs<ExchangeErrorContractEventArgs>;
|
||||
const errCode = log.args.errorId;
|
||||
expect(errCode).to.be.equal(ExchangeContractErrs.ERROR_ORDER_EXPIRED);
|
||||
const log = res.logs[0] as LogWithDecodedArgs<ExchangeStatusContractEventArgs>;
|
||||
const statusCode = log.args.statusId;
|
||||
expect(statusCode).to.be.equal(ExchangeStatus.ORDER_EXPIRED);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
831
packages/contracts/test/exchange/match_orders.ts
Normal file
831
packages/contracts/test/exchange/match_orders.ts
Normal file
@@ -0,0 +1,831 @@
|
||||
import { LogWithDecodedArgs, ZeroEx } from '0x.js';
|
||||
import { BlockchainLifecycle } from '@0xproject/dev-utils';
|
||||
import { BigNumber } from '@0xproject/utils';
|
||||
import * as chai from 'chai';
|
||||
import ethUtil = require('ethereumjs-util');
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { DummyERC20TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c20_token';
|
||||
import { DummyERC721TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c721_token';
|
||||
import { ERC20ProxyContract } from '../../src/contract_wrappers/generated/e_r_c20_proxy';
|
||||
import { ERC721ProxyContract } from '../../src/contract_wrappers/generated/e_r_c721_proxy';
|
||||
import {
|
||||
CancelContractEventArgs,
|
||||
ExchangeContract,
|
||||
ExchangeStatusContractEventArgs,
|
||||
FillContractEventArgs,
|
||||
} from '../../src/contract_wrappers/generated/exchange';
|
||||
import { artifacts } from '../../src/utils/artifacts';
|
||||
import { assetProxyUtils } from '../../src/utils/asset_proxy_utils';
|
||||
import { chaiSetup } from '../../src/utils/chai_setup';
|
||||
import { constants } from '../../src/utils/constants';
|
||||
import { crypto } from '../../src/utils/crypto';
|
||||
import { ERC20Wrapper } from '../../src/utils/erc20_wrapper';
|
||||
import { ERC721Wrapper } from '../../src/utils/erc721_wrapper';
|
||||
import { ExchangeWrapper } from '../../src/utils/exchange_wrapper';
|
||||
import { OrderFactory } from '../../src/utils/order_factory';
|
||||
import { orderUtils } from '../../src/utils/order_utils';
|
||||
import {
|
||||
AssetProxyId,
|
||||
ContractName,
|
||||
ERC20BalancesByOwner,
|
||||
ERC721TokenIdsByOwner,
|
||||
ExchangeStatus,
|
||||
OrderInfo,
|
||||
SignedOrder,
|
||||
} from '../../src/utils/types';
|
||||
import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper';
|
||||
|
||||
import { MatchOrderTester } from '../utils/match_order_tester';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
|
||||
describe('matchOrders', () => {
|
||||
let makerAddressLeft: string;
|
||||
let makerAddressRight: string;
|
||||
let owner: string;
|
||||
let takerAddress: string;
|
||||
let feeRecipientAddressLeft: string;
|
||||
let feeRecipientAddressRight: string;
|
||||
|
||||
let erc20TokenA: DummyERC20TokenContract;
|
||||
let erc20TokenB: DummyERC20TokenContract;
|
||||
let zrxToken: DummyERC20TokenContract;
|
||||
let erc721Token: DummyERC721TokenContract;
|
||||
let exchange: ExchangeContract;
|
||||
let erc20Proxy: ERC20ProxyContract;
|
||||
let erc721Proxy: ERC721ProxyContract;
|
||||
|
||||
let erc20BalancesByOwner: ERC20BalancesByOwner;
|
||||
let erc721TokenIdsByOwner: ERC721TokenIdsByOwner;
|
||||
let exchangeWrapper: ExchangeWrapper;
|
||||
let erc20Wrapper: ERC20Wrapper;
|
||||
let erc721Wrapper: ERC721Wrapper;
|
||||
let orderFactoryLeft: OrderFactory;
|
||||
let orderFactoryRight: OrderFactory;
|
||||
|
||||
let erc721LeftMakerAssetIds: BigNumber[];
|
||||
let erc721RightMakerAssetIds: BigNumber[];
|
||||
let erc721TakerAssetIds: BigNumber[];
|
||||
|
||||
let defaultERC20MakerAssetAddress: string;
|
||||
let defaultERC20TakerAssetAddress: string;
|
||||
let defaultERC721AssetAddress: string;
|
||||
|
||||
let matchOrderTester: MatchOrderTester;
|
||||
|
||||
let zeroEx: ZeroEx;
|
||||
|
||||
before(async () => {
|
||||
// Create accounts
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
const usedAddresses = ([
|
||||
owner,
|
||||
makerAddressLeft,
|
||||
makerAddressRight,
|
||||
takerAddress,
|
||||
feeRecipientAddressLeft,
|
||||
feeRecipientAddressRight,
|
||||
] = accounts);
|
||||
// Create wrappers
|
||||
erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
|
||||
erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner);
|
||||
// Deploy ERC20 token & ERC20 proxy
|
||||
[erc20TokenA, erc20TokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync();
|
||||
erc20Proxy = await erc20Wrapper.deployProxyAsync();
|
||||
await erc20Wrapper.setBalancesAndAllowancesAsync();
|
||||
// Deploy ERC721 token and proxy
|
||||
[erc721Token] = await erc721Wrapper.deployDummyTokensAsync();
|
||||
erc721Proxy = await erc721Wrapper.deployProxyAsync();
|
||||
await erc721Wrapper.setBalancesAndAllowancesAsync();
|
||||
const erc721Balances = await erc721Wrapper.getBalancesAsync();
|
||||
erc721LeftMakerAssetIds = erc721Balances[makerAddressLeft][erc721Token.address];
|
||||
erc721RightMakerAssetIds = erc721Balances[makerAddressRight][erc721Token.address];
|
||||
erc721TakerAssetIds = erc721Balances[takerAddress][erc721Token.address];
|
||||
// Depoy exchange
|
||||
exchange = await ExchangeContract.deployFrom0xArtifactAsync(
|
||||
artifacts.Exchange,
|
||||
provider,
|
||||
txDefaults,
|
||||
assetProxyUtils.encodeERC20ProxyData(zrxToken.address),
|
||||
);
|
||||
zeroEx = new ZeroEx(provider, {
|
||||
exchangeContractAddress: exchange.address,
|
||||
networkId: constants.TESTRPC_NETWORK_ID,
|
||||
});
|
||||
exchangeWrapper = new ExchangeWrapper(exchange, zeroEx);
|
||||
await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC20, erc20Proxy.address, owner);
|
||||
await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC721, erc721Proxy.address, owner);
|
||||
// Authorize ERC20 and ERC721 trades by exchange
|
||||
await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, {
|
||||
from: owner,
|
||||
});
|
||||
await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, {
|
||||
from: owner,
|
||||
});
|
||||
// Set default addresses
|
||||
defaultERC20MakerAssetAddress = erc20TokenA.address;
|
||||
defaultERC20TakerAssetAddress = erc20TokenB.address;
|
||||
defaultERC721AssetAddress = erc721Token.address;
|
||||
// Create default order parameters
|
||||
const defaultOrderParams = {
|
||||
...constants.STATIC_ORDER_PARAMS,
|
||||
exchangeAddress: exchange.address,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
};
|
||||
const privateKeyLeft = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressLeft)];
|
||||
orderFactoryLeft = new OrderFactory(privateKeyLeft, defaultOrderParams);
|
||||
const privateKeyRight = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressRight)];
|
||||
orderFactoryRight = new OrderFactory(privateKeyRight, defaultOrderParams);
|
||||
// Set match order tester
|
||||
matchOrderTester = new MatchOrderTester(exchangeWrapper, erc20Wrapper, erc721Wrapper, zrxToken.address);
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
describe('matchOrders', () => {
|
||||
beforeEach(async () => {
|
||||
erc20BalancesByOwner = await erc20Wrapper.getBalancesAsync();
|
||||
erc721TokenIdsByOwner = await erc721Wrapper.getBalancesAsync();
|
||||
});
|
||||
|
||||
it('should transfer the correct amounts when orders completely fill each other', async () => {
|
||||
// Create orders to match
|
||||
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
|
||||
makerAddress: makerAddressLeft,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(5), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
feeRecipientAddress: feeRecipientAddressLeft,
|
||||
});
|
||||
const signedOrderRight = orderFactoryRight.newSignedOrder({
|
||||
makerAddress: makerAddressRight,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(2), 18),
|
||||
feeRecipientAddress: feeRecipientAddressRight,
|
||||
});
|
||||
// Match signedOrderLeft with signedOrderRight
|
||||
await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
||||
signedOrderLeft,
|
||||
signedOrderRight,
|
||||
takerAddress,
|
||||
erc20BalancesByOwner,
|
||||
erc721TokenIdsByOwner,
|
||||
);
|
||||
// Verify left order was fully filled
|
||||
const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft);
|
||||
expect(leftOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
|
||||
// Verify right order was fully filled
|
||||
const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight);
|
||||
expect(rightOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
|
||||
});
|
||||
|
||||
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 = orderFactoryLeft.newSignedOrder({
|
||||
makerAddress: makerAddressLeft,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(5), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
feeRecipientAddress: feeRecipientAddressLeft,
|
||||
});
|
||||
const signedOrderRight = orderFactoryRight.newSignedOrder({
|
||||
makerAddress: makerAddressRight,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(5), 18),
|
||||
feeRecipientAddress: feeRecipientAddressRight,
|
||||
});
|
||||
// Store original taker balance
|
||||
const takerInitialBalances = _.cloneDeep(erc20BalancesByOwner[takerAddress][defaultERC20MakerAssetAddress]);
|
||||
// Match signedOrderLeft with signedOrderRight
|
||||
let newERC20BalancesByOwner: ERC20BalancesByOwner;
|
||||
let newERC721TokenIdsByOwner: ERC721TokenIdsByOwner;
|
||||
[
|
||||
newERC20BalancesByOwner,
|
||||
newERC721TokenIdsByOwner,
|
||||
] = await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
||||
signedOrderLeft,
|
||||
signedOrderRight,
|
||||
takerAddress,
|
||||
erc20BalancesByOwner,
|
||||
erc721TokenIdsByOwner,
|
||||
);
|
||||
// Verify left order was fully filled
|
||||
const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft);
|
||||
expect(leftOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
|
||||
// Verify right order was fully filled
|
||||
const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight);
|
||||
expect(rightOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
|
||||
// Verify taker did not take a profit
|
||||
expect(takerInitialBalances).to.be.deep.equal(
|
||||
newERC20BalancesByOwner[takerAddress][defaultERC20MakerAssetAddress],
|
||||
);
|
||||
});
|
||||
|
||||
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 = orderFactoryLeft.newSignedOrder({
|
||||
makerAddress: makerAddressLeft,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(5), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
feeRecipientAddress: feeRecipientAddressLeft,
|
||||
});
|
||||
const signedOrderRight = orderFactoryRight.newSignedOrder({
|
||||
makerAddress: makerAddressRight,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(20), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(4), 18),
|
||||
feeRecipientAddress: feeRecipientAddressRight,
|
||||
});
|
||||
// Match orders
|
||||
await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
||||
signedOrderLeft,
|
||||
signedOrderRight,
|
||||
takerAddress,
|
||||
erc20BalancesByOwner,
|
||||
erc721TokenIdsByOwner,
|
||||
);
|
||||
// Verify left order was fully filled
|
||||
const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft);
|
||||
expect(leftOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
|
||||
// Verify right order was partially filled
|
||||
const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight);
|
||||
expect(rightOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FILLABLE);
|
||||
});
|
||||
|
||||
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 = orderFactoryLeft.newSignedOrder({
|
||||
makerAddress: makerAddressLeft,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(50), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
|
||||
feeRecipientAddress: feeRecipientAddressLeft,
|
||||
});
|
||||
const signedOrderRight = orderFactoryRight.newSignedOrder({
|
||||
makerAddress: makerAddressRight,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(2), 18),
|
||||
feeRecipientAddress: feeRecipientAddressRight,
|
||||
});
|
||||
// Match orders
|
||||
await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
||||
signedOrderLeft,
|
||||
signedOrderRight,
|
||||
takerAddress,
|
||||
erc20BalancesByOwner,
|
||||
erc721TokenIdsByOwner,
|
||||
);
|
||||
// Verify left order was partially filled
|
||||
const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft);
|
||||
expect(leftOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FILLABLE);
|
||||
// Verify right order was fully filled
|
||||
const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight);
|
||||
expect(rightOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
|
||||
});
|
||||
|
||||
it('should transfer the correct amounts when consecutive calls are used to completely fill the left order', async () => {
|
||||
// Create orders to match
|
||||
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
|
||||
makerAddress: makerAddressLeft,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(50), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
|
||||
feeRecipientAddress: feeRecipientAddressLeft,
|
||||
});
|
||||
const signedOrderRight = orderFactoryRight.newSignedOrder({
|
||||
makerAddress: makerAddressRight,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(2), 18),
|
||||
feeRecipientAddress: feeRecipientAddressRight,
|
||||
});
|
||||
// Match orders
|
||||
let newERC20BalancesByOwner: ERC20BalancesByOwner;
|
||||
let newERC721TokenIdsByOwner: ERC721TokenIdsByOwner;
|
||||
[
|
||||
newERC20BalancesByOwner,
|
||||
newERC721TokenIdsByOwner,
|
||||
] = await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
||||
signedOrderLeft,
|
||||
signedOrderRight,
|
||||
takerAddress,
|
||||
erc20BalancesByOwner,
|
||||
erc721TokenIdsByOwner,
|
||||
);
|
||||
// Verify left order was partially filled
|
||||
const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft);
|
||||
expect(leftOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FILLABLE);
|
||||
// Verify right order was fully filled
|
||||
const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight);
|
||||
expect(rightOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
|
||||
// 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 = orderFactoryRight.newSignedOrder({
|
||||
makerAddress: makerAddressRight,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(50), 18),
|
||||
feeRecipientAddress: feeRecipientAddressRight,
|
||||
});
|
||||
// Match signedOrderLeft with signedOrderRight2
|
||||
const leftTakerAssetFilledAmount = signedOrderRight.makerAssetAmount;
|
||||
const rightTakerAssetFilledAmount = new BigNumber(0);
|
||||
await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
||||
signedOrderLeft,
|
||||
signedOrderRight2,
|
||||
takerAddress,
|
||||
newERC20BalancesByOwner,
|
||||
erc721TokenIdsByOwner,
|
||||
leftTakerAssetFilledAmount,
|
||||
rightTakerAssetFilledAmount,
|
||||
);
|
||||
// Verify left order was fully filled
|
||||
const leftOrderInfo2: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft);
|
||||
expect(leftOrderInfo2.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
|
||||
// Verify second right order was partially filled
|
||||
const rightOrderInfo2: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight2);
|
||||
expect(rightOrderInfo2.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FILLABLE);
|
||||
});
|
||||
|
||||
it('should transfer the correct amounts when consecutive calls are used to completely fill the right order', async () => {
|
||||
// Create orders to match
|
||||
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
|
||||
makerAddress: makerAddressLeft,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(2), 18),
|
||||
feeRecipientAddress: feeRecipientAddressLeft,
|
||||
});
|
||||
|
||||
const signedOrderRight = orderFactoryRight.newSignedOrder({
|
||||
makerAddress: makerAddressRight,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(50), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
|
||||
feeRecipientAddress: feeRecipientAddressRight,
|
||||
});
|
||||
// Match orders
|
||||
let newERC20BalancesByOwner: ERC20BalancesByOwner;
|
||||
let newERC721TokenIdsByOwner: ERC721TokenIdsByOwner;
|
||||
[
|
||||
newERC20BalancesByOwner,
|
||||
newERC721TokenIdsByOwner,
|
||||
] = await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
||||
signedOrderLeft,
|
||||
signedOrderRight,
|
||||
takerAddress,
|
||||
erc20BalancesByOwner,
|
||||
erc721TokenIdsByOwner,
|
||||
);
|
||||
// Verify left order was partially filled
|
||||
const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft);
|
||||
expect(leftOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
|
||||
// Verify right order was fully filled
|
||||
const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight);
|
||||
expect(rightOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FILLABLE);
|
||||
// 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 = orderFactoryLeft.newSignedOrder({
|
||||
makerAddress: makerAddressLeft,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(50), 18),
|
||||
feeRecipientAddress: feeRecipientAddressLeft,
|
||||
});
|
||||
// Match signedOrderLeft2 with signedOrderRight
|
||||
const leftTakerAssetFilledAmount = new BigNumber(0);
|
||||
const takerAmountReceived = newERC20BalancesByOwner[takerAddress][defaultERC20MakerAssetAddress].minus(
|
||||
erc20BalancesByOwner[takerAddress][defaultERC20MakerAssetAddress],
|
||||
);
|
||||
const rightTakerAssetFilledAmount = signedOrderLeft.makerAssetAmount.minus(takerAmountReceived);
|
||||
await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
||||
signedOrderLeft2,
|
||||
signedOrderRight,
|
||||
takerAddress,
|
||||
newERC20BalancesByOwner,
|
||||
erc721TokenIdsByOwner,
|
||||
leftTakerAssetFilledAmount,
|
||||
rightTakerAssetFilledAmount,
|
||||
);
|
||||
// Verify second left order was partially filled
|
||||
const leftOrderInfo2: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft2);
|
||||
expect(leftOrderInfo2.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FILLABLE);
|
||||
// Verify right order was fully filled
|
||||
const rightOrderInfo2: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight);
|
||||
expect(rightOrderInfo2.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
|
||||
});
|
||||
|
||||
it('should transfer the correct amounts if fee recipient is the same across both matched orders', async () => {
|
||||
const feeRecipientAddress = feeRecipientAddressLeft;
|
||||
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
|
||||
makerAddress: makerAddressLeft,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(5), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
feeRecipientAddress,
|
||||
});
|
||||
const signedOrderRight = orderFactoryRight.newSignedOrder({
|
||||
makerAddress: makerAddressRight,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(2), 18),
|
||||
feeRecipientAddress,
|
||||
});
|
||||
// Match orders
|
||||
await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
||||
signedOrderLeft,
|
||||
signedOrderRight,
|
||||
takerAddress,
|
||||
erc20BalancesByOwner,
|
||||
erc721TokenIdsByOwner,
|
||||
);
|
||||
});
|
||||
|
||||
it('should transfer the correct amounts if taker is also the left order maker', async () => {
|
||||
// Create orders to match
|
||||
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
|
||||
makerAddress: makerAddressLeft,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(5), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
feeRecipientAddress: feeRecipientAddressLeft,
|
||||
});
|
||||
const signedOrderRight = orderFactoryRight.newSignedOrder({
|
||||
makerAddress: makerAddressRight,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(2), 18),
|
||||
feeRecipientAddress: feeRecipientAddressRight,
|
||||
});
|
||||
// Match orders
|
||||
takerAddress = signedOrderLeft.makerAddress;
|
||||
await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
||||
signedOrderLeft,
|
||||
signedOrderRight,
|
||||
takerAddress,
|
||||
erc20BalancesByOwner,
|
||||
erc721TokenIdsByOwner,
|
||||
);
|
||||
});
|
||||
|
||||
it('should transfer the correct amounts if taker is also the right order maker', async () => {
|
||||
// Create orders to match
|
||||
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
|
||||
makerAddress: makerAddressLeft,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(5), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
feeRecipientAddress: feeRecipientAddressLeft,
|
||||
});
|
||||
const signedOrderRight = orderFactoryRight.newSignedOrder({
|
||||
makerAddress: makerAddressRight,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(2), 18),
|
||||
feeRecipientAddress: feeRecipientAddressRight,
|
||||
});
|
||||
// Match orders
|
||||
takerAddress = signedOrderRight.makerAddress;
|
||||
await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
||||
signedOrderLeft,
|
||||
signedOrderRight,
|
||||
takerAddress,
|
||||
erc20BalancesByOwner,
|
||||
erc721TokenIdsByOwner,
|
||||
);
|
||||
});
|
||||
|
||||
it('should transfer the correct amounts if taker is also the left fee recipient', async () => {
|
||||
// Create orders to match
|
||||
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
|
||||
makerAddress: makerAddressLeft,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(5), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
feeRecipientAddress: feeRecipientAddressLeft,
|
||||
});
|
||||
const signedOrderRight = orderFactoryRight.newSignedOrder({
|
||||
makerAddress: makerAddressRight,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(2), 18),
|
||||
feeRecipientAddress: feeRecipientAddressRight,
|
||||
});
|
||||
// Match orders
|
||||
takerAddress = feeRecipientAddressLeft;
|
||||
await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
||||
signedOrderLeft,
|
||||
signedOrderRight,
|
||||
takerAddress,
|
||||
erc20BalancesByOwner,
|
||||
erc721TokenIdsByOwner,
|
||||
);
|
||||
});
|
||||
|
||||
it('should transfer the correct amounts if taker is also the right fee recipient', async () => {
|
||||
// Create orders to match
|
||||
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
|
||||
makerAddress: makerAddressLeft,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(5), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
feeRecipientAddress: feeRecipientAddressLeft,
|
||||
});
|
||||
const signedOrderRight = orderFactoryRight.newSignedOrder({
|
||||
makerAddress: makerAddressRight,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(2), 18),
|
||||
feeRecipientAddress: feeRecipientAddressRight,
|
||||
});
|
||||
// Match orders
|
||||
takerAddress = feeRecipientAddressRight;
|
||||
await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
||||
signedOrderLeft,
|
||||
signedOrderRight,
|
||||
takerAddress,
|
||||
erc20BalancesByOwner,
|
||||
erc721TokenIdsByOwner,
|
||||
);
|
||||
});
|
||||
|
||||
it('should transfer the correct amounts if left maker is the left fee recipient and right maker is the right fee recipient', async () => {
|
||||
// Create orders to match
|
||||
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
|
||||
makerAddress: makerAddressLeft,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(5), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
feeRecipientAddress: makerAddressLeft,
|
||||
});
|
||||
const signedOrderRight = orderFactoryRight.newSignedOrder({
|
||||
makerAddress: makerAddressRight,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(2), 18),
|
||||
feeRecipientAddress: makerAddressRight,
|
||||
});
|
||||
// Match orders
|
||||
await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
||||
signedOrderLeft,
|
||||
signedOrderRight,
|
||||
takerAddress,
|
||||
erc20BalancesByOwner,
|
||||
erc721TokenIdsByOwner,
|
||||
);
|
||||
});
|
||||
|
||||
it('Should throw if left order is not fillable', async () => {
|
||||
// Create orders to match
|
||||
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
|
||||
makerAddress: makerAddressLeft,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(5), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
feeRecipientAddress: feeRecipientAddressLeft,
|
||||
});
|
||||
const signedOrderRight = orderFactoryRight.newSignedOrder({
|
||||
makerAddress: makerAddressRight,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(2), 18),
|
||||
feeRecipientAddress: feeRecipientAddressRight,
|
||||
});
|
||||
// Cancel left order
|
||||
await exchangeWrapper.cancelOrderAsync(signedOrderLeft, signedOrderLeft.makerAddress);
|
||||
// Match orders
|
||||
return expect(
|
||||
exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress),
|
||||
).to.be.rejectedWith(constants.REVERT);
|
||||
});
|
||||
|
||||
it('Should throw if right order is not fillable', async () => {
|
||||
// Create orders to match
|
||||
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
|
||||
makerAddress: makerAddressLeft,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(5), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
feeRecipientAddress: feeRecipientAddressLeft,
|
||||
});
|
||||
const signedOrderRight = orderFactoryRight.newSignedOrder({
|
||||
makerAddress: makerAddressRight,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(2), 18),
|
||||
feeRecipientAddress: feeRecipientAddressRight,
|
||||
});
|
||||
// Cancel right order
|
||||
await exchangeWrapper.cancelOrderAsync(signedOrderRight, signedOrderRight.makerAddress);
|
||||
// Match orders
|
||||
return expect(
|
||||
exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress),
|
||||
).to.be.rejectedWith(constants.REVERT);
|
||||
});
|
||||
|
||||
it('should throw if there is not a positive spread', async () => {
|
||||
// Create orders to match
|
||||
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
|
||||
makerAddress: makerAddressLeft,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(5), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
|
||||
feeRecipientAddress: feeRecipientAddressLeft,
|
||||
});
|
||||
const signedOrderRight = orderFactoryRight.newSignedOrder({
|
||||
makerAddress: makerAddressRight,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
|
||||
feeRecipientAddress: feeRecipientAddressRight,
|
||||
});
|
||||
// Match orders
|
||||
return expect(
|
||||
matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
||||
signedOrderLeft,
|
||||
signedOrderRight,
|
||||
takerAddress,
|
||||
erc20BalancesByOwner,
|
||||
erc721TokenIdsByOwner,
|
||||
),
|
||||
).to.be.rejectedWith(constants.REVERT);
|
||||
});
|
||||
|
||||
it('should throw if the left maker asset is not equal to the right taker asset ', async () => {
|
||||
// Create orders to match
|
||||
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
|
||||
makerAddress: makerAddressLeft,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(5), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
feeRecipientAddress: feeRecipientAddressLeft,
|
||||
});
|
||||
const signedOrderRight = orderFactoryRight.newSignedOrder({
|
||||
makerAddress: makerAddressRight,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(2), 18),
|
||||
feeRecipientAddress: feeRecipientAddressRight,
|
||||
});
|
||||
// Match orders
|
||||
return expect(
|
||||
matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
||||
signedOrderLeft,
|
||||
signedOrderRight,
|
||||
takerAddress,
|
||||
erc20BalancesByOwner,
|
||||
erc721TokenIdsByOwner,
|
||||
),
|
||||
).to.be.rejectedWith(constants.REVERT);
|
||||
});
|
||||
|
||||
it('should throw if the right maker asset is not equal to the left taker asset', async () => {
|
||||
// Create orders to match
|
||||
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
|
||||
makerAddress: makerAddressLeft,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(5), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
feeRecipientAddress: feeRecipientAddressLeft,
|
||||
});
|
||||
const signedOrderRight = orderFactoryRight.newSignedOrder({
|
||||
makerAddress: makerAddressRight,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(2), 18),
|
||||
feeRecipientAddress: feeRecipientAddressRight,
|
||||
});
|
||||
// Match orders
|
||||
return expect(
|
||||
matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
||||
signedOrderLeft,
|
||||
signedOrderRight,
|
||||
takerAddress,
|
||||
erc20BalancesByOwner,
|
||||
erc721TokenIdsByOwner,
|
||||
),
|
||||
).to.be.rejectedWith(constants.REVERT);
|
||||
});
|
||||
|
||||
it('should transfer correct amounts when left order maker asset is an ERC721 token', async () => {
|
||||
// Create orders to match
|
||||
const erc721TokenToTransfer = erc721LeftMakerAssetIds[0];
|
||||
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
|
||||
makerAddress: makerAddressLeft,
|
||||
makerAssetData: assetProxyUtils.encodeERC721ProxyData(defaultERC721AssetAddress, erc721TokenToTransfer),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
makerAssetAmount: new BigNumber(1),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
feeRecipientAddress: feeRecipientAddressLeft,
|
||||
});
|
||||
const signedOrderRight = orderFactoryRight.newSignedOrder({
|
||||
makerAddress: makerAddressRight,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC721ProxyData(defaultERC721AssetAddress, erc721TokenToTransfer),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
takerAssetAmount: new BigNumber(1),
|
||||
feeRecipientAddress: feeRecipientAddressRight,
|
||||
});
|
||||
// Match orders
|
||||
await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
||||
signedOrderLeft,
|
||||
signedOrderRight,
|
||||
takerAddress,
|
||||
erc20BalancesByOwner,
|
||||
erc721TokenIdsByOwner,
|
||||
);
|
||||
// Verify left order was fully filled
|
||||
const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft);
|
||||
expect(leftOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
|
||||
// Verify right order was fully filled
|
||||
const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight);
|
||||
expect(rightOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
|
||||
});
|
||||
|
||||
it('should transfer correct amounts when right order maker asset is an ERC721 token', async () => {
|
||||
// Create orders to match
|
||||
const erc721TokenToTransfer = erc721RightMakerAssetIds[0];
|
||||
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
|
||||
makerAddress: makerAddressLeft,
|
||||
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
takerAssetData: assetProxyUtils.encodeERC721ProxyData(defaultERC721AssetAddress, erc721TokenToTransfer),
|
||||
makerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
takerAssetAmount: new BigNumber(1),
|
||||
feeRecipientAddress: feeRecipientAddressLeft,
|
||||
});
|
||||
const signedOrderRight = orderFactoryRight.newSignedOrder({
|
||||
makerAddress: makerAddressRight,
|
||||
makerAssetData: assetProxyUtils.encodeERC721ProxyData(defaultERC721AssetAddress, erc721TokenToTransfer),
|
||||
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
|
||||
makerAssetAmount: new BigNumber(1),
|
||||
takerAssetAmount: ZeroEx.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
feeRecipientAddress: feeRecipientAddressRight,
|
||||
});
|
||||
// Match orders
|
||||
await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
||||
signedOrderLeft,
|
||||
signedOrderRight,
|
||||
takerAddress,
|
||||
erc20BalancesByOwner,
|
||||
erc721TokenIdsByOwner,
|
||||
);
|
||||
// Verify left order was fully filled
|
||||
const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft);
|
||||
expect(leftOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
|
||||
// Verify right order was fully filled
|
||||
const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight);
|
||||
expect(rightOrderInfo.orderStatus as ExchangeStatus).to.be.equal(ExchangeStatus.ORDER_FULLY_FILLED);
|
||||
});
|
||||
});
|
||||
}); // tslint:disable-line:max-file-line-count
|
||||
@@ -21,7 +21,7 @@ import { TransactionFactory } from '../../src/utils/transaction_factory';
|
||||
import {
|
||||
AssetProxyId,
|
||||
ERC20BalancesByOwner,
|
||||
ExchangeContractErrs,
|
||||
ExchangeStatus,
|
||||
OrderStruct,
|
||||
SignatureType,
|
||||
SignedOrder,
|
||||
@@ -197,7 +197,7 @@ describe('Exchange transactions', () => {
|
||||
|
||||
it('should cancel the order when signed by maker and called by sender', async () => {
|
||||
await exchangeWrapper.executeTransactionAsync(signedTx, senderAddress);
|
||||
const res = await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
|
||||
const res = await exchangeWrapper.fillOrderAsync(signedOrder, senderAddress);
|
||||
const newBalances = await erc20Wrapper.getBalancesAsync();
|
||||
expect(newBalances).to.deep.equal(erc20Balances);
|
||||
});
|
||||
|
||||
353
packages/contracts/test/utils/match_order_tester.ts
Normal file
353
packages/contracts/test/utils/match_order_tester.ts
Normal file
@@ -0,0 +1,353 @@
|
||||
import { LogWithDecodedArgs, ZeroEx } from '0x.js';
|
||||
import { BlockchainLifecycle } from '@0xproject/dev-utils';
|
||||
import { BigNumber } from '@0xproject/utils';
|
||||
import * as chai from 'chai';
|
||||
import ethUtil = require('ethereumjs-util');
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { DummyERC20TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c20_token';
|
||||
import { DummyERC721TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c721_token';
|
||||
import { ERC20ProxyContract } from '../../src/contract_wrappers/generated/e_r_c20_proxy';
|
||||
import { ERC721ProxyContract } from '../../src/contract_wrappers/generated/e_r_c721_proxy';
|
||||
import {
|
||||
CancelContractEventArgs,
|
||||
ExchangeContract,
|
||||
FillContractEventArgs,
|
||||
} from '../../src/contract_wrappers/generated/exchange';
|
||||
import { assetProxyUtils } from '../../src/utils/asset_proxy_utils';
|
||||
import { chaiSetup } from '../../src/utils/chai_setup';
|
||||
import { constants } from '../../src/utils/constants';
|
||||
import { crypto } from '../../src/utils/crypto';
|
||||
import { ERC20Wrapper } from '../../src/utils/erc20_wrapper';
|
||||
import { ERC721Wrapper } from '../../src/utils/erc721_wrapper';
|
||||
import { ExchangeWrapper } from '../../src/utils/exchange_wrapper';
|
||||
import { OrderFactory } from '../../src/utils/order_factory';
|
||||
import { orderUtils } from '../../src/utils/order_utils';
|
||||
import {
|
||||
AssetProxyId,
|
||||
ContractName,
|
||||
ERC20BalancesByOwner,
|
||||
ERC721TokenIdsByOwner,
|
||||
ExchangeStatus,
|
||||
SignedOrder,
|
||||
TransferAmountsByMatchOrders as TransferAmounts,
|
||||
} from '../../src/utils/types';
|
||||
import { provider, web3Wrapper } from '../../src/utils/web3_wrapper';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
|
||||
export class MatchOrderTester {
|
||||
private _exchangeWrapper: ExchangeWrapper;
|
||||
private _erc20Wrapper: ERC20Wrapper;
|
||||
private _erc721Wrapper: ERC721Wrapper;
|
||||
private _feeTokenAddress: string;
|
||||
|
||||
/// @dev Compares a pair of ERC20 balances and a pair of ERC721 token owners.
|
||||
/// @param expectedNewERC20BalancesByOwner Expected ERC20 balances.
|
||||
/// @param realERC20BalancesByOwner Actual ERC20 balances.
|
||||
/// @param expectedNewERC721TokenIdsByOwner Expected ERC721 token owners.
|
||||
/// @param realERC721TokenIdsByOwner Actual ERC20 token owners.
|
||||
/// @return True only if ERC20 balances match and ERC721 token owners match.
|
||||
private static _compareExpectedAndRealBalances(
|
||||
expectedNewERC20BalancesByOwner: ERC20BalancesByOwner,
|
||||
realERC20BalancesByOwner: ERC20BalancesByOwner,
|
||||
expectedNewERC721TokenIdsByOwner: ERC721TokenIdsByOwner,
|
||||
realERC721TokenIdsByOwner: ERC721TokenIdsByOwner,
|
||||
) {
|
||||
// ERC20 Balances
|
||||
const erc20BalancesMatch = _.isEqual(expectedNewERC20BalancesByOwner, realERC20BalancesByOwner);
|
||||
if (!erc20BalancesMatch) {
|
||||
return false;
|
||||
}
|
||||
// ERC721 Token Ids
|
||||
const sortedExpectedNewERC721TokenIdsByOwner = _.mapValues(
|
||||
expectedNewERC721TokenIdsByOwner,
|
||||
tokenIdsByOwner => {
|
||||
_.mapValues(tokenIdsByOwner, tokenIds => {
|
||||
_.sortBy(tokenIds);
|
||||
});
|
||||
},
|
||||
);
|
||||
const sortedNewERC721TokenIdsByOwner = _.mapValues(realERC721TokenIdsByOwner, tokenIdsByOwner => {
|
||||
_.mapValues(tokenIdsByOwner, tokenIds => {
|
||||
_.sortBy(tokenIds);
|
||||
});
|
||||
});
|
||||
const erc721TokenIdsMatch = _.isEqual(sortedExpectedNewERC721TokenIdsByOwner, sortedNewERC721TokenIdsByOwner);
|
||||
return erc721TokenIdsMatch;
|
||||
}
|
||||
/// @dev Constructs new MatchOrderTester.
|
||||
/// @param exchangeWrapper Used to call to the Exchange.
|
||||
/// @param erc20Wrapper Used to fetch ERC20 balances.
|
||||
/// @param erc721Wrapper Used to fetch ERC721 token owners.
|
||||
/// @param feeTokenAddress Address of ERC20 fee token.
|
||||
constructor(
|
||||
exchangeWrapper: ExchangeWrapper,
|
||||
erc20Wrapper: ERC20Wrapper,
|
||||
erc721Wrapper: ERC721Wrapper,
|
||||
feeTokenAddress: string,
|
||||
) {
|
||||
this._exchangeWrapper = exchangeWrapper;
|
||||
this._erc20Wrapper = erc20Wrapper;
|
||||
this._erc721Wrapper = erc721Wrapper;
|
||||
this._feeTokenAddress = feeTokenAddress;
|
||||
}
|
||||
/// @dev Matches two complementary orders and validates results.
|
||||
/// Validation either succeeds or throws.
|
||||
/// @param signedOrderLeft First matched order.
|
||||
/// @param signedOrderRight Second matched order.
|
||||
/// @param takerAddress Address of taker (the address who matched the two orders)
|
||||
/// @param erc20BalancesByOwner Current ERC20 balances.
|
||||
/// @param erc721TokenIdsByOwner Current ERC721 token owners.
|
||||
/// @param initialTakerAssetFilledAmountLeft Current amount the left order has been filled.
|
||||
/// @param initialTakerAssetFilledAmountRight Current amount the right order has been filled.
|
||||
/// @return New ERC20 balances & ERC721 token owners.
|
||||
public async matchOrdersAndVerifyBalancesAsync(
|
||||
signedOrderLeft: SignedOrder,
|
||||
signedOrderRight: SignedOrder,
|
||||
takerAddress: string,
|
||||
erc20BalancesByOwner: ERC20BalancesByOwner,
|
||||
erc721TokenIdsByOwner: ERC721TokenIdsByOwner,
|
||||
initialTakerAssetFilledAmountLeft?: BigNumber,
|
||||
initialTakerAssetFilledAmountRight?: BigNumber,
|
||||
): Promise<[ERC20BalancesByOwner, ERC721TokenIdsByOwner]> {
|
||||
// Test setup & verify preconditions
|
||||
const makerAddressLeft = signedOrderLeft.makerAddress;
|
||||
const makerAddressRight = signedOrderRight.makerAddress;
|
||||
const feeRecipientAddressLeft = signedOrderLeft.feeRecipientAddress;
|
||||
const feeRecipientAddressRight = signedOrderRight.feeRecipientAddress;
|
||||
// Verify Left order preconditions
|
||||
const orderTakerAssetFilledAmountLeft = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
|
||||
orderUtils.getOrderHashHex(signedOrderLeft),
|
||||
);
|
||||
const expectedOrderFilledAmountLeft = initialTakerAssetFilledAmountLeft
|
||||
? initialTakerAssetFilledAmountLeft
|
||||
: new BigNumber(0);
|
||||
expect(expectedOrderFilledAmountLeft).to.be.bignumber.equal(orderTakerAssetFilledAmountLeft);
|
||||
// Verify Right order preconditions
|
||||
const orderTakerAssetFilledAmountRight = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
|
||||
orderUtils.getOrderHashHex(signedOrderRight),
|
||||
);
|
||||
const expectedOrderFilledAmountRight = initialTakerAssetFilledAmountRight
|
||||
? initialTakerAssetFilledAmountRight
|
||||
: new BigNumber(0);
|
||||
expect(expectedOrderFilledAmountRight).to.be.bignumber.equal(orderTakerAssetFilledAmountRight);
|
||||
// Match left & right orders
|
||||
await this._exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress);
|
||||
const newERC20BalancesByOwner = await this._erc20Wrapper.getBalancesAsync();
|
||||
const newERC721TokenIdsByOwner = await this._erc721Wrapper.getBalancesAsync();
|
||||
// Calculate expected balance changes
|
||||
const expectedTransferAmounts = await this._calculateExpectedTransferAmountsAsync(
|
||||
signedOrderLeft,
|
||||
signedOrderRight,
|
||||
orderTakerAssetFilledAmountLeft,
|
||||
orderTakerAssetFilledAmountRight,
|
||||
);
|
||||
let expectedERC20BalancesByOwner: ERC20BalancesByOwner;
|
||||
let expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner;
|
||||
[expectedERC20BalancesByOwner, expectedERC721TokenIdsByOwner] = this._calculateExpectedBalances(
|
||||
signedOrderLeft,
|
||||
signedOrderRight,
|
||||
takerAddress,
|
||||
erc20BalancesByOwner,
|
||||
erc721TokenIdsByOwner,
|
||||
expectedTransferAmounts,
|
||||
);
|
||||
// Assert our expected balances are equal to the actual balances
|
||||
const expectedBalancesMatchRealBalances = MatchOrderTester._compareExpectedAndRealBalances(
|
||||
expectedERC20BalancesByOwner,
|
||||
newERC20BalancesByOwner,
|
||||
expectedERC721TokenIdsByOwner,
|
||||
newERC721TokenIdsByOwner,
|
||||
);
|
||||
expect(expectedBalancesMatchRealBalances).to.be.true();
|
||||
return [newERC20BalancesByOwner, newERC721TokenIdsByOwner];
|
||||
}
|
||||
/// @dev Calculates expected transfer amounts between order makers, fee recipients, and
|
||||
/// the taker when two orders are matched.
|
||||
/// @param signedOrderLeft First matched order.
|
||||
/// @param signedOrderRight Second matched order.
|
||||
/// @param orderTakerAssetFilledAmountLeft How much left order has been filled, prior to matching orders.
|
||||
/// @param orderTakerAssetFilledAmountRight How much the right order has been filled, prior to matching orders.
|
||||
/// @return TransferAmounts A struct containing the expected transfer amounts.
|
||||
private async _calculateExpectedTransferAmountsAsync(
|
||||
signedOrderLeft: SignedOrder,
|
||||
signedOrderRight: SignedOrder,
|
||||
orderTakerAssetFilledAmountLeft: BigNumber,
|
||||
orderTakerAssetFilledAmountRight: BigNumber,
|
||||
): Promise<TransferAmounts> {
|
||||
let amountBoughtByLeftMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
|
||||
orderUtils.getOrderHashHex(signedOrderLeft),
|
||||
);
|
||||
amountBoughtByLeftMaker = amountBoughtByLeftMaker.minus(orderTakerAssetFilledAmountLeft);
|
||||
const amountSoldByLeftMaker = amountBoughtByLeftMaker
|
||||
.times(signedOrderLeft.makerAssetAmount)
|
||||
.dividedToIntegerBy(signedOrderLeft.takerAssetAmount);
|
||||
const amountReceivedByRightMaker = amountBoughtByLeftMaker
|
||||
.times(signedOrderRight.takerAssetAmount)
|
||||
.dividedToIntegerBy(signedOrderRight.makerAssetAmount);
|
||||
const amountReceivedByTaker = amountSoldByLeftMaker.minus(amountReceivedByRightMaker);
|
||||
let amountBoughtByRightMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
|
||||
orderUtils.getOrderHashHex(signedOrderRight),
|
||||
);
|
||||
amountBoughtByRightMaker = amountBoughtByRightMaker.minus(orderTakerAssetFilledAmountRight);
|
||||
const amountSoldByRightMaker = amountBoughtByRightMaker
|
||||
.times(signedOrderRight.makerAssetAmount)
|
||||
.dividedToIntegerBy(signedOrderRight.takerAssetAmount);
|
||||
const amountReceivedByLeftMaker = amountSoldByRightMaker;
|
||||
const feePaidByLeftMaker = signedOrderLeft.makerFee
|
||||
.times(amountSoldByLeftMaker)
|
||||
.dividedToIntegerBy(signedOrderLeft.makerAssetAmount);
|
||||
const feePaidByRightMaker = signedOrderRight.makerFee
|
||||
.times(amountSoldByRightMaker)
|
||||
.dividedToIntegerBy(signedOrderRight.makerAssetAmount);
|
||||
const feePaidByTakerLeft = signedOrderLeft.takerFee
|
||||
.times(amountSoldByLeftMaker)
|
||||
.dividedToIntegerBy(signedOrderLeft.makerAssetAmount);
|
||||
const feePaidByTakerRight = signedOrderRight.takerFee
|
||||
.times(amountSoldByRightMaker)
|
||||
.dividedToIntegerBy(signedOrderRight.makerAssetAmount);
|
||||
const totalFeePaidByTaker = feePaidByTakerLeft.add(feePaidByTakerRight);
|
||||
const feeReceivedLeft = feePaidByLeftMaker.add(feePaidByTakerLeft);
|
||||
const feeReceivedRight = feePaidByRightMaker.add(feePaidByTakerRight);
|
||||
// Return values
|
||||
const expectedTransferAmounts = {
|
||||
// Left Maker
|
||||
amountBoughtByLeftMaker,
|
||||
amountSoldByLeftMaker,
|
||||
amountReceivedByLeftMaker,
|
||||
feePaidByLeftMaker,
|
||||
// Right Maker
|
||||
amountBoughtByRightMaker,
|
||||
amountSoldByRightMaker,
|
||||
amountReceivedByRightMaker,
|
||||
feePaidByRightMaker,
|
||||
// Taker
|
||||
amountReceivedByTaker,
|
||||
feePaidByTakerLeft,
|
||||
feePaidByTakerRight,
|
||||
totalFeePaidByTaker,
|
||||
// Fee Recipients
|
||||
feeReceivedLeft,
|
||||
feeReceivedRight,
|
||||
};
|
||||
return expectedTransferAmounts;
|
||||
}
|
||||
/// @dev Calculates the expected balances of order makers, fee recipients, and the taker,
|
||||
/// as a result of matching two orders.
|
||||
/// @param signedOrderLeft First matched order.
|
||||
/// @param signedOrderRight Second matched order.
|
||||
/// @param takerAddress Address of taker (the address who matched the two orders)
|
||||
/// @param erc20BalancesByOwner Current ERC20 balances.
|
||||
/// @param erc721TokenIdsByOwner Current ERC721 token owners.
|
||||
/// @param expectedTransferAmounts A struct containing the expected transfer amounts.
|
||||
/// @return Expected ERC20 balances & ERC721 token owners after orders have been matched.
|
||||
private _calculateExpectedBalances(
|
||||
signedOrderLeft: SignedOrder,
|
||||
signedOrderRight: SignedOrder,
|
||||
takerAddress: string,
|
||||
erc20BalancesByOwner: ERC20BalancesByOwner,
|
||||
erc721TokenIdsByOwner: ERC721TokenIdsByOwner,
|
||||
expectedTransferAmounts: TransferAmounts,
|
||||
): [ERC20BalancesByOwner, ERC721TokenIdsByOwner] {
|
||||
const makerAddressLeft = signedOrderLeft.makerAddress;
|
||||
const makerAddressRight = signedOrderRight.makerAddress;
|
||||
const feeRecipientAddressLeft = signedOrderLeft.feeRecipientAddress;
|
||||
const feeRecipientAddressRight = signedOrderRight.feeRecipientAddress;
|
||||
// Operations are performed on copies of the balances
|
||||
const expectedNewERC20BalancesByOwner = _.cloneDeep(erc20BalancesByOwner);
|
||||
const expectedNewERC721TokenIdsByOwner = _.cloneDeep(erc721TokenIdsByOwner);
|
||||
// Left Maker Asset (Right Taker Asset)
|
||||
const makerAssetProxyIdLeft = assetProxyUtils.decodeProxyDataId(signedOrderLeft.makerAssetData);
|
||||
if (makerAssetProxyIdLeft === AssetProxyId.ERC20) {
|
||||
// Decode asset data
|
||||
const erc20ProxyData = assetProxyUtils.decodeERC20ProxyData(signedOrderLeft.makerAssetData);
|
||||
const makerAssetAddressLeft = erc20ProxyData.tokenAddress;
|
||||
const takerAssetAddressRight = makerAssetAddressLeft;
|
||||
// Left Maker
|
||||
expectedNewERC20BalancesByOwner[makerAddressLeft][makerAssetAddressLeft] = expectedNewERC20BalancesByOwner[
|
||||
makerAddressLeft
|
||||
][makerAssetAddressLeft].minus(expectedTransferAmounts.amountSoldByLeftMaker);
|
||||
// Right Maker
|
||||
expectedNewERC20BalancesByOwner[makerAddressRight][
|
||||
takerAssetAddressRight
|
||||
] = expectedNewERC20BalancesByOwner[makerAddressRight][takerAssetAddressRight].add(
|
||||
expectedTransferAmounts.amountReceivedByRightMaker,
|
||||
);
|
||||
// Taker
|
||||
expectedNewERC20BalancesByOwner[takerAddress][makerAssetAddressLeft] = expectedNewERC20BalancesByOwner[
|
||||
takerAddress
|
||||
][makerAssetAddressLeft].add(expectedTransferAmounts.amountReceivedByTaker);
|
||||
} else if (makerAssetProxyIdLeft === AssetProxyId.ERC721) {
|
||||
// Decode asset data
|
||||
const erc721ProxyData = assetProxyUtils.decodeERC721ProxyData(signedOrderLeft.makerAssetData);
|
||||
const makerAssetAddressLeft = erc721ProxyData.tokenAddress;
|
||||
const makerAssetIdLeft = erc721ProxyData.tokenId;
|
||||
const takerAssetAddressRight = makerAssetAddressLeft;
|
||||
const takerAssetIdRight = makerAssetIdLeft;
|
||||
// Left Maker
|
||||
_.remove(expectedNewERC721TokenIdsByOwner[makerAddressLeft][makerAssetAddressLeft], makerAssetIdLeft);
|
||||
// Right Maker
|
||||
expectedNewERC721TokenIdsByOwner[makerAddressRight][takerAssetAddressRight].push(takerAssetIdRight);
|
||||
// Taker: Since there is only 1 asset transferred, the taker does not receive any of the left maker asset.
|
||||
}
|
||||
// Left Taker Asset (Right Maker Asset)
|
||||
// Note: This exchange is only between the order makers: the Taker does not receive any of the left taker asset.
|
||||
const takerAssetProxyIdLeft = assetProxyUtils.decodeProxyDataId(signedOrderLeft.takerAssetData);
|
||||
if (takerAssetProxyIdLeft === AssetProxyId.ERC20) {
|
||||
// Decode asset data
|
||||
const erc20ProxyData = assetProxyUtils.decodeERC20ProxyData(signedOrderLeft.takerAssetData);
|
||||
const takerAssetAddressLeft = erc20ProxyData.tokenAddress;
|
||||
const makerAssetAddressRight = takerAssetAddressLeft;
|
||||
// Left Maker
|
||||
expectedNewERC20BalancesByOwner[makerAddressLeft][takerAssetAddressLeft] = expectedNewERC20BalancesByOwner[
|
||||
makerAddressLeft
|
||||
][takerAssetAddressLeft].add(expectedTransferAmounts.amountReceivedByLeftMaker);
|
||||
// Right Maker
|
||||
expectedNewERC20BalancesByOwner[makerAddressRight][
|
||||
makerAssetAddressRight
|
||||
] = expectedNewERC20BalancesByOwner[makerAddressRight][makerAssetAddressRight].minus(
|
||||
expectedTransferAmounts.amountSoldByRightMaker,
|
||||
);
|
||||
} else if (takerAssetProxyIdLeft === AssetProxyId.ERC721) {
|
||||
// Decode asset data
|
||||
const erc721ProxyData = assetProxyUtils.decodeERC721ProxyData(signedOrderRight.makerAssetData);
|
||||
const makerAssetAddressRight = erc721ProxyData.tokenAddress;
|
||||
const makerAssetIdRight = erc721ProxyData.tokenId;
|
||||
const takerAssetAddressLeft = makerAssetAddressRight;
|
||||
const takerAssetIdLeft = makerAssetIdRight;
|
||||
// Right Maker
|
||||
_.remove(expectedNewERC721TokenIdsByOwner[makerAddressRight][makerAssetAddressRight], makerAssetIdRight);
|
||||
// Left Maker
|
||||
expectedNewERC721TokenIdsByOwner[makerAddressLeft][takerAssetAddressLeft].push(takerAssetIdLeft);
|
||||
}
|
||||
// Left Maker Fees
|
||||
expectedNewERC20BalancesByOwner[makerAddressLeft][this._feeTokenAddress] = expectedNewERC20BalancesByOwner[
|
||||
makerAddressLeft
|
||||
][this._feeTokenAddress].minus(expectedTransferAmounts.feePaidByLeftMaker);
|
||||
// Right Maker Fees
|
||||
expectedNewERC20BalancesByOwner[makerAddressRight][this._feeTokenAddress] = expectedNewERC20BalancesByOwner[
|
||||
makerAddressRight
|
||||
][this._feeTokenAddress].minus(expectedTransferAmounts.feePaidByRightMaker);
|
||||
// Taker Fees
|
||||
expectedNewERC20BalancesByOwner[takerAddress][this._feeTokenAddress] = expectedNewERC20BalancesByOwner[
|
||||
takerAddress
|
||||
][this._feeTokenAddress].minus(expectedTransferAmounts.totalFeePaidByTaker);
|
||||
// Left Fee Recipient Fees
|
||||
expectedNewERC20BalancesByOwner[feeRecipientAddressLeft][
|
||||
this._feeTokenAddress
|
||||
] = expectedNewERC20BalancesByOwner[feeRecipientAddressLeft][this._feeTokenAddress].add(
|
||||
expectedTransferAmounts.feeReceivedLeft,
|
||||
);
|
||||
// Right Fee Recipient Fees
|
||||
expectedNewERC20BalancesByOwner[feeRecipientAddressRight][
|
||||
this._feeTokenAddress
|
||||
] = expectedNewERC20BalancesByOwner[feeRecipientAddressRight][this._feeTokenAddress].add(
|
||||
expectedTransferAmounts.feeReceivedRight,
|
||||
);
|
||||
|
||||
return [expectedNewERC20BalancesByOwner, expectedNewERC721TokenIdsByOwner];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user