aboutsummaryrefslogtreecommitdiffstats
path: root/contracts/extensions/test/balance_threshold_filter.ts
diff options
context:
space:
mode:
Diffstat (limited to 'contracts/extensions/test/balance_threshold_filter.ts')
-rw-r--r--contracts/extensions/test/balance_threshold_filter.ts1637
1 files changed, 1637 insertions, 0 deletions
diff --git a/contracts/extensions/test/balance_threshold_filter.ts b/contracts/extensions/test/balance_threshold_filter.ts
new file mode 100644
index 000000000..c79a72f5a
--- /dev/null
+++ b/contracts/extensions/test/balance_threshold_filter.ts
@@ -0,0 +1,1637 @@
+import { BlockchainLifecycle } from '@0x/dev-utils';
+import { assetDataUtils } from '@0x/order-utils';
+import { Order, RevertReason, SignedOrder } from '@0x/types';
+import { BigNumber } from '@0x/utils';
+import { Web3Wrapper } from '@0x/web3-wrapper';
+import * as chai from 'chai';
+import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
+import * as _ from 'lodash';
+
+import { ERC20Wrapper, ERC721Wrapper } from '@0x/contracts-asset-proxy';
+import { DummyERC20TokenContract } from '@0x/contracts-erc20';
+import { artifacts as exchangeArtifacts, ExchangeContract, ExchangeWrapper } from '@0x/contracts-exchange';
+import {
+ chaiSetup,
+ constants,
+ ContractName,
+ ERC20BalancesByOwner,
+ expectTransactionFailedAsync,
+ OrderFactory,
+ OrderStatus,
+ provider,
+ TransactionFactory,
+ txDefaults,
+ web3Wrapper,
+} from '@0x/contracts-test-utils';
+
+import { artifacts, BalanceThresholdFilterContract, BalanceThresholdWrapper } from '../src';
+
+chaiSetup.configure();
+const expect = chai.expect;
+const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
+const DECIMALS_DEFAULT = 18;
+
+interface ValidatedAddressesLog {
+ args: { addresses: string[] };
+}
+
+describe(ContractName.BalanceThresholdFilter, () => {
+ const takerAssetAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(500), DECIMALS_DEFAULT);
+ const makerAssetAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), DECIMALS_DEFAULT);
+ const takerAssetFillAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(250), DECIMALS_DEFAULT);
+
+ let validMakerAddress: string;
+ let validMakerAddress2: string;
+ let owner: string;
+ let validTakerAddress: string;
+ let feeRecipientAddress: string;
+ let invalidAddress: string;
+ let defaultMakerAssetAddress: string;
+ let defaultTakerAssetAddress: string;
+ let zrxAssetData: string;
+ let zrxToken: DummyERC20TokenContract;
+ let exchangeInstance: ExchangeContract;
+ let exchangeWrapper: ExchangeWrapper;
+
+ let orderFactory: OrderFactory;
+ let orderFactory2: OrderFactory;
+ let invalidOrderFactory: OrderFactory;
+ let erc20Wrapper: ERC20Wrapper;
+ let erc20Balances: ERC20BalancesByOwner;
+ let erc20TakerBalanceThresholdWrapper: BalanceThresholdWrapper;
+ let erc721TakerBalanceThresholdWrapper: BalanceThresholdWrapper;
+ let erc721MakerBalanceThresholdWrapper: BalanceThresholdWrapper;
+ let erc721NonValidBalanceThresholdWrapper: BalanceThresholdWrapper;
+
+ let defaultOrderParams: Partial<Order>;
+ let validSignedOrder: SignedOrder;
+ let validSignedOrder2: SignedOrder;
+
+ let erc721BalanceThresholdFilterInstance: BalanceThresholdFilterContract;
+ let erc20BalanceThresholdFilterInstance: BalanceThresholdFilterContract;
+
+ const assertValidatedAddressesLog = async (
+ txReceipt: TransactionReceiptWithDecodedLogs,
+ expectedValidatedAddresses: string[],
+ ) => {
+ expect(txReceipt.logs.length).to.be.gte(1);
+ const validatedAddressesLog = (txReceipt.logs[0] as any) as ValidatedAddressesLog;
+ const validatedAddresses = validatedAddressesLog.args.addresses;
+ // @HACK-hysz: Nested addresses are not translated to lower-case but this will change once
+ // the new ABI Encoder/Decoder is used by the contract templates.
+ const validatedAddressesNormalized: string[] = [];
+ _.each(validatedAddresses, address => {
+ const normalizedAddress = _.toLower(address);
+ validatedAddressesNormalized.push(normalizedAddress);
+ });
+ expect(validatedAddressesNormalized).to.be.deep.equal(expectedValidatedAddresses);
+ };
+
+ before(async () => {
+ // Create accounts
+ await blockchainLifecycle.startAsync();
+ const accounts = await web3Wrapper.getAvailableAddressesAsync();
+ const usedAddresses = ([
+ owner,
+ validMakerAddress,
+ validMakerAddress2,
+ validTakerAddress,
+ feeRecipientAddress,
+ invalidAddress,
+ ] = accounts);
+ const takerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(validTakerAddress)];
+ const makerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(validMakerAddress)];
+ const secondMakerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(validMakerAddress2)];
+ const invalidAddressPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(invalidAddress)];
+ // Create wrappers
+ erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
+ const validAddresses = _.cloneDeepWith(usedAddresses);
+ _.remove(validAddresses, (address: string) => {
+ return address === invalidAddress;
+ });
+ const erc721Wrapper = new ERC721Wrapper(provider, validAddresses, owner);
+ // Deploy ERC20 tokens
+ const numDummyErc20ToDeploy = 4;
+ let erc20TokenA: DummyERC20TokenContract;
+ let erc20TokenB: DummyERC20TokenContract;
+ let erc20BalanceThresholdAsset: DummyERC20TokenContract;
+ [erc20TokenA, erc20TokenB, zrxToken, erc20BalanceThresholdAsset] = await erc20Wrapper.deployDummyTokensAsync(
+ numDummyErc20ToDeploy,
+ constants.DUMMY_TOKEN_DECIMALS,
+ );
+ defaultMakerAssetAddress = erc20TokenA.address;
+ defaultTakerAssetAddress = erc20TokenB.address;
+ zrxAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address);
+ // Create proxies
+ const erc20Proxy = await erc20Wrapper.deployProxyAsync();
+ await erc20Wrapper.setBalancesAndAllowancesAsync();
+ // Deploy Exchange contract
+ exchangeInstance = await ExchangeContract.deployFrom0xArtifactAsync(
+ exchangeArtifacts.Exchange,
+ provider,
+ txDefaults,
+ zrxAssetData,
+ );
+ exchangeWrapper = new ExchangeWrapper(exchangeInstance, provider);
+ // Register proxies
+ await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner);
+ await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeInstance.address, {
+ from: owner,
+ });
+ // Deploy Balance Threshold Filters
+ // One uses an ERC721 token as its balance threshold asset; the other uses an ERC20
+ const erc721alanceThreshold = new BigNumber(1);
+ await erc721Wrapper.deployProxyAsync();
+ const [erc721BalanceThresholdAsset] = await erc721Wrapper.deployDummyTokensAsync();
+ await erc721Wrapper.setBalancesAndAllowancesAsync();
+ erc721BalanceThresholdFilterInstance = await BalanceThresholdFilterContract.deployFrom0xArtifactAsync(
+ artifacts.BalanceThresholdFilter,
+ provider,
+ txDefaults,
+ exchangeInstance.address,
+ erc721BalanceThresholdAsset.address,
+ erc721alanceThreshold,
+ );
+ const erc20BalanceThreshold = Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 10);
+ erc20BalanceThresholdFilterInstance = await BalanceThresholdFilterContract.deployFrom0xArtifactAsync(
+ artifacts.BalanceThresholdFilter,
+ provider,
+ txDefaults,
+ exchangeInstance.address,
+ erc20BalanceThresholdAsset.address,
+ erc20BalanceThreshold,
+ );
+ // Default order parameters
+ defaultOrderParams = {
+ exchangeAddress: exchangeInstance.address,
+ feeRecipientAddress,
+ makerAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
+ takerAssetData: assetDataUtils.encodeERC20AssetData(defaultTakerAssetAddress),
+ makerAssetAmount,
+ takerAssetAmount,
+ makerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), DECIMALS_DEFAULT),
+ takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(150), DECIMALS_DEFAULT),
+ senderAddress: erc721BalanceThresholdFilterInstance.address,
+ };
+ // Create two order factories with valid makers (who meet the threshold balance), and
+ // one factory for an invalid address (that does not meet the threshold balance)
+ // Valid order factory #1
+ const defaultOrderParams1 = {
+ makerAddress: validMakerAddress,
+ ...defaultOrderParams,
+ };
+ orderFactory = new OrderFactory(makerPrivateKey, defaultOrderParams1);
+ // Valid order factory #2
+ const defaultOrderParams2 = {
+ makerAddress: validMakerAddress2,
+ ...defaultOrderParams,
+ };
+ orderFactory2 = new OrderFactory(secondMakerPrivateKey, defaultOrderParams2);
+ // Invalid order factory
+ const defaultNonValidOrderParams = {
+ makerAddress: invalidAddress,
+ ...defaultOrderParams,
+ };
+ invalidOrderFactory = new OrderFactory(invalidAddressPrivateKey, defaultNonValidOrderParams);
+ // Create Balance Thresold Wrappers
+ erc20TakerBalanceThresholdWrapper = new BalanceThresholdWrapper(
+ erc20BalanceThresholdFilterInstance,
+ exchangeInstance,
+ new TransactionFactory(takerPrivateKey, exchangeInstance.address),
+ provider,
+ );
+ erc721TakerBalanceThresholdWrapper = new BalanceThresholdWrapper(
+ erc721BalanceThresholdFilterInstance,
+ exchangeInstance,
+ new TransactionFactory(takerPrivateKey, exchangeInstance.address),
+ provider,
+ );
+ erc721MakerBalanceThresholdWrapper = new BalanceThresholdWrapper(
+ erc721BalanceThresholdFilterInstance,
+ exchangeInstance,
+ new TransactionFactory(makerPrivateKey, exchangeInstance.address),
+ provider,
+ );
+ erc721NonValidBalanceThresholdWrapper = new BalanceThresholdWrapper(
+ erc721BalanceThresholdFilterInstance,
+ exchangeInstance,
+ new TransactionFactory(invalidAddressPrivateKey, exchangeInstance.address),
+ provider,
+ );
+ });
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+
+ describe('General Sanity Checks', () => {
+ beforeEach(async () => {
+ erc20Balances = await erc20Wrapper.getBalancesAsync();
+ validSignedOrder = await orderFactory.newSignedOrderAsync();
+ validSignedOrder2 = await orderFactory2.newSignedOrderAsync();
+ });
+ it('should transfer the correct amounts and validate both maker/taker when both maker and taker exceed the balance threshold of an ERC20 token', async () => {
+ const validSignedOrderERC20Sender = await orderFactory.newSignedOrderAsync({
+ ...defaultOrderParams,
+ makerAddress: validMakerAddress,
+ senderAddress: erc20TakerBalanceThresholdWrapper.getBalanceThresholdAddress(),
+ });
+ // Execute a valid fill
+ const txReceipt = await erc20TakerBalanceThresholdWrapper.fillOrderAsync(
+ validSignedOrderERC20Sender,
+ validTakerAddress,
+ { takerAssetFillAmount },
+ );
+ // Assert validated addresses
+ const expectedValidatedAddresseses = [validSignedOrder.makerAddress, validTakerAddress];
+ await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses);
+ // Check balances
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ const makerAssetFillAmount = takerAssetFillAmount
+ .times(validSignedOrder.makerAssetAmount)
+ .dividedToIntegerBy(validSignedOrder.takerAssetAmount);
+ const makerFeePaid = validSignedOrder.makerFee
+ .times(makerAssetFillAmount)
+ .dividedToIntegerBy(validSignedOrder.makerAssetAmount);
+ const takerFeePaid = validSignedOrder.takerFee
+ .times(makerAssetFillAmount)
+ .dividedToIntegerBy(validSignedOrder.makerAssetAmount);
+ expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount),
+ );
+ expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][defaultTakerAssetAddress].plus(takerAssetFillAmount),
+ );
+ expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][zrxToken.address].minus(makerFeePaid),
+ );
+ expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount),
+ );
+ expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount),
+ );
+ expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid),
+ );
+ expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[feeRecipientAddress][zrxToken.address].plus(makerFeePaid.plus(takerFeePaid)),
+ );
+ });
+ it('should revert if the Exchange transaction function is not supported', async () => {
+ // Create signed order without the fillOrder function selector
+ const salt = new BigNumber(0);
+ const badSelectorHex = '0x00000000';
+ const signatureHex = '0x';
+ // Call valid forwarder
+ return expectTransactionFailedAsync(
+ erc721BalanceThresholdFilterInstance.executeTransaction.sendTransactionAsync(
+ salt,
+ validTakerAddress,
+ badSelectorHex,
+ signatureHex,
+ ),
+ RevertReason.InvalidOrBlockedExchangeSelector,
+ );
+ });
+ it('should revert if senderAddress is not set to the valid forwarding contract', async () => {
+ // Create signed order with incorrect senderAddress
+ const notBalanceThresholdFilterAddress = zrxToken.address;
+ const signedOrderWithBadSenderAddress = await orderFactory.newSignedOrderAsync({
+ senderAddress: notBalanceThresholdFilterAddress,
+ });
+ // Call valid forwarder
+ return expectTransactionFailedAsync(
+ erc721TakerBalanceThresholdWrapper.fillOrderAsync(signedOrderWithBadSenderAddress, validTakerAddress, {
+ takerAssetFillAmount,
+ }),
+ RevertReason.FailedExecution,
+ );
+ });
+ });
+
+ describe('batchFillOrders', () => {
+ beforeEach(async () => {
+ erc20Balances = await erc20Wrapper.getBalancesAsync();
+ validSignedOrder = await orderFactory.newSignedOrderAsync();
+ validSignedOrder2 = await orderFactory2.newSignedOrderAsync();
+ });
+ it('should transfer the correct amounts and validate both makers/taker when both maker and taker meet the balance threshold', async () => {
+ // Execute a valid fill
+ const orders = [validSignedOrder, validSignedOrder2];
+ const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount];
+ const txReceipt = await erc721TakerBalanceThresholdWrapper.batchFillOrdersAsync(orders, validTakerAddress, {
+ takerAssetFillAmounts,
+ });
+ // Assert validated addresses
+ const expectedValidatedAddresseses = [
+ validSignedOrder.makerAddress,
+ validSignedOrder2.makerAddress,
+ validTakerAddress,
+ ];
+ await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses);
+ // Check balances
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ const cumulativeTakerAssetFillAmount = takerAssetFillAmount.times(2);
+ const makerAssetFillAmount = takerAssetFillAmount
+ .times(validSignedOrder.makerAssetAmount)
+ .dividedToIntegerBy(validSignedOrder.takerAssetAmount);
+ const makerFeePaid = validSignedOrder.makerFee
+ .times(makerAssetFillAmount)
+ .dividedToIntegerBy(validSignedOrder.makerAssetAmount);
+ const takerFeePaid = validSignedOrder.takerFee
+ .times(makerAssetFillAmount)
+ .dividedToIntegerBy(validSignedOrder.makerAssetAmount)
+ .times(2);
+ // Maker #1
+ expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount),
+ );
+ expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][defaultTakerAssetAddress].plus(takerAssetFillAmount),
+ );
+ expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][zrxToken.address].minus(makerFeePaid),
+ );
+ // Maker #2
+ expect(newBalances[validMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount),
+ );
+ expect(newBalances[validMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress2][defaultTakerAssetAddress].plus(takerAssetFillAmount),
+ );
+ expect(newBalances[validMakerAddress2][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress2][zrxToken.address].minus(makerFeePaid),
+ );
+ // Taker
+ expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount),
+ );
+
+ expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount.times(2)),
+ );
+ expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid),
+ );
+ // Fee recipient
+ expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[feeRecipientAddress][zrxToken.address].plus(makerFeePaid.times(2).plus(takerFeePaid)),
+ );
+ });
+ it('should revert if one maker does not meet the balance threshold', async () => {
+ // Create order set with one non-valid maker address
+ const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount];
+ const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({
+ makerAddress: invalidAddress,
+ });
+ const orders = [validSignedOrder, signedOrderWithBadMakerAddress];
+ // Execute transaction
+ return expectTransactionFailedAsync(
+ erc721TakerBalanceThresholdWrapper.batchFillOrdersAsync(orders, validTakerAddress, {
+ takerAssetFillAmounts,
+ }),
+ RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold,
+ );
+ });
+ it('should revert if taker does not meet the balance threshold', async () => {
+ const orders = [validSignedOrder, validSignedOrder2];
+ const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount];
+ return expectTransactionFailedAsync(
+ erc721NonValidBalanceThresholdWrapper.batchFillOrdersAsync(orders, invalidAddress, {
+ takerAssetFillAmounts,
+ }),
+ RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold,
+ );
+ });
+ });
+
+ describe('batchFillOrdersNoThrow', () => {
+ beforeEach(async () => {
+ erc20Balances = await erc20Wrapper.getBalancesAsync();
+ validSignedOrder = await orderFactory.newSignedOrderAsync();
+ validSignedOrder2 = await orderFactory2.newSignedOrderAsync();
+ });
+ it('should transfer the correct amounts and validate both makers/taker when both maker and taker meet the balance threshold', async () => {
+ // Execute a valid fill
+ const orders = [validSignedOrder, validSignedOrder2];
+ const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount];
+ const txReceipt = await erc721TakerBalanceThresholdWrapper.batchFillOrdersNoThrowAsync(
+ orders,
+ validTakerAddress,
+ {
+ takerAssetFillAmounts,
+ // HACK(albrow): We need to hardcode the gas estimate here because
+ // the Geth gas estimator doesn't work with the way we use
+ // delegatecall and swallow errors.
+ gas: 600000,
+ },
+ );
+ // Assert validated addresses
+ const expectedValidatedAddresseses = [
+ validSignedOrder.makerAddress,
+ validSignedOrder2.makerAddress,
+ validTakerAddress,
+ ];
+ await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses);
+ // Check balances
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ const cumulativeTakerAssetFillAmount = takerAssetFillAmount.times(2);
+ const makerAssetFillAmount = takerAssetFillAmount
+ .times(validSignedOrder.makerAssetAmount)
+ .dividedToIntegerBy(validSignedOrder.takerAssetAmount);
+ const makerFeePaid = validSignedOrder.makerFee
+ .times(makerAssetFillAmount)
+ .dividedToIntegerBy(validSignedOrder.makerAssetAmount);
+ const takerFeePaid = validSignedOrder.takerFee
+ .times(makerAssetFillAmount)
+ .dividedToIntegerBy(validSignedOrder.makerAssetAmount)
+ .times(2);
+ // Maker #1
+ expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount),
+ );
+ expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][defaultTakerAssetAddress].plus(takerAssetFillAmount),
+ );
+ expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][zrxToken.address].minus(makerFeePaid),
+ );
+ // Maker #2
+ expect(newBalances[validMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount),
+ );
+ expect(newBalances[validMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress2][defaultTakerAssetAddress].plus(takerAssetFillAmount),
+ );
+ expect(newBalances[validMakerAddress2][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress2][zrxToken.address].minus(makerFeePaid),
+ );
+ // Taker
+ expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount),
+ );
+
+ expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount.times(2)),
+ );
+ expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid),
+ );
+ // Fee recipient
+ expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[feeRecipientAddress][zrxToken.address].plus(makerFeePaid.times(2).plus(takerFeePaid)),
+ );
+ });
+ it('should revert if one maker does not meet the balance threshold', async () => {
+ // Create order set with one non-valid maker address
+ const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount];
+ const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({
+ makerAddress: invalidAddress,
+ });
+ const orders = [validSignedOrder, signedOrderWithBadMakerAddress];
+ // Execute transaction
+ return expectTransactionFailedAsync(
+ erc721TakerBalanceThresholdWrapper.batchFillOrdersNoThrowAsync(orders, validTakerAddress, {
+ takerAssetFillAmounts,
+ }),
+ RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold,
+ );
+ });
+ it('should revert if taker does not meet the balance threshold', async () => {
+ const orders = [validSignedOrder, validSignedOrder2];
+ const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount];
+ return expectTransactionFailedAsync(
+ erc721NonValidBalanceThresholdWrapper.batchFillOrdersNoThrowAsync(orders, invalidAddress, {
+ takerAssetFillAmounts,
+ }),
+ RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold,
+ );
+ });
+ });
+
+ describe('batchFillOrKillOrders', () => {
+ beforeEach(async () => {
+ erc20Balances = await erc20Wrapper.getBalancesAsync();
+ validSignedOrder = await orderFactory.newSignedOrderAsync();
+ validSignedOrder2 = await orderFactory2.newSignedOrderAsync();
+ });
+ it('should transfer the correct amounts and validate both makers/taker when both makers and taker meet the balance threshold', async () => {
+ // Execute a valid fill
+ const orders = [validSignedOrder, validSignedOrder2];
+ const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount];
+ const txReceipt = await erc721TakerBalanceThresholdWrapper.batchFillOrKillOrdersAsync(
+ orders,
+ validTakerAddress,
+ { takerAssetFillAmounts },
+ );
+ // Assert validated addresses
+ const expectedValidatedAddresseses = [
+ validSignedOrder.makerAddress,
+ validSignedOrder2.makerAddress,
+ validTakerAddress,
+ ];
+ await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses);
+ // Check balances
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ const cumulativeTakerAssetFillAmount = takerAssetFillAmount.times(2);
+ const makerAssetFillAmount = takerAssetFillAmount
+ .times(validSignedOrder.makerAssetAmount)
+ .dividedToIntegerBy(validSignedOrder.takerAssetAmount);
+ const makerFeePaid = validSignedOrder.makerFee
+ .times(makerAssetFillAmount)
+ .dividedToIntegerBy(validSignedOrder.makerAssetAmount);
+ const takerFeePaid = validSignedOrder.takerFee
+ .times(makerAssetFillAmount)
+ .dividedToIntegerBy(validSignedOrder.makerAssetAmount)
+ .times(2);
+ // Maker #1
+ expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount),
+ );
+ expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][defaultTakerAssetAddress].plus(takerAssetFillAmount),
+ );
+ expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][zrxToken.address].minus(makerFeePaid),
+ );
+ // Maker #2
+ expect(newBalances[validMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount),
+ );
+ expect(newBalances[validMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress2][defaultTakerAssetAddress].plus(takerAssetFillAmount),
+ );
+ expect(newBalances[validMakerAddress2][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress2][zrxToken.address].minus(makerFeePaid),
+ );
+ // Taker
+ expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount),
+ );
+
+ expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount.times(2)),
+ );
+ expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid),
+ );
+ // Fee recipient
+ expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[feeRecipientAddress][zrxToken.address].plus(makerFeePaid.times(2).plus(takerFeePaid)),
+ );
+ });
+ it('should revert if one maker does not meet the balance threshold', async () => {
+ // Create order set with one non-valid maker address
+ const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount];
+ const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({
+ makerAddress: invalidAddress,
+ });
+ const orders = [validSignedOrder, signedOrderWithBadMakerAddress];
+ // Execute transaction
+ return expectTransactionFailedAsync(
+ erc721TakerBalanceThresholdWrapper.batchFillOrKillOrdersAsync(orders, validTakerAddress, {
+ takerAssetFillAmounts,
+ }),
+ RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold,
+ );
+ });
+ it('should revert if taker does not meet the balance threshold', async () => {
+ const orders = [validSignedOrder, validSignedOrder2];
+ const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount];
+ return expectTransactionFailedAsync(
+ erc721NonValidBalanceThresholdWrapper.batchFillOrKillOrdersAsync(orders, invalidAddress, {
+ takerAssetFillAmounts,
+ }),
+ RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold,
+ );
+ });
+ it('should revert if one takerAssetFillAmount is not fully filled', async () => {
+ const tooBigTakerAssetFillAmount = validSignedOrder.takerAssetAmount.times(2);
+ const orders = [validSignedOrder, validSignedOrder2];
+ const takerAssetFillAmounts = [takerAssetFillAmount, tooBigTakerAssetFillAmount];
+ return expectTransactionFailedAsync(
+ erc721TakerBalanceThresholdWrapper.batchFillOrKillOrdersAsync(orders, validTakerAddress, {
+ takerAssetFillAmounts,
+ }),
+ RevertReason.FailedExecution,
+ );
+ });
+ });
+
+ describe('fillOrder', () => {
+ beforeEach(async () => {
+ erc20Balances = await erc20Wrapper.getBalancesAsync();
+ validSignedOrder = await orderFactory.newSignedOrderAsync();
+ });
+ it('should transfer the correct amounts and validate both maker/taker when both maker and taker meet the balance threshold', async () => {
+ // Execute a valid fill
+ const txReceipt = await erc721TakerBalanceThresholdWrapper.fillOrderAsync(
+ validSignedOrder,
+ validTakerAddress,
+ { takerAssetFillAmount },
+ );
+ // Assert validated addresses
+ const expectedValidatedAddresseses = [validSignedOrder.makerAddress, validTakerAddress];
+ await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses);
+ // Check balances
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ const makerAssetFillAmount = takerAssetFillAmount
+ .times(validSignedOrder.makerAssetAmount)
+ .dividedToIntegerBy(validSignedOrder.takerAssetAmount);
+ const makerFeePaid = validSignedOrder.makerFee
+ .times(makerAssetFillAmount)
+ .dividedToIntegerBy(validSignedOrder.makerAssetAmount);
+ const takerFeePaid = validSignedOrder.takerFee
+ .times(makerAssetFillAmount)
+ .dividedToIntegerBy(validSignedOrder.makerAssetAmount);
+ expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount),
+ );
+ expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][defaultTakerAssetAddress].plus(takerAssetFillAmount),
+ );
+ expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][zrxToken.address].minus(makerFeePaid),
+ );
+ expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount),
+ );
+ expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount),
+ );
+ expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid),
+ );
+ expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[feeRecipientAddress][zrxToken.address].plus(makerFeePaid.plus(takerFeePaid)),
+ );
+ });
+ it('should revert if maker does not meet the balance threshold', async () => {
+ // Create signed order with non-valid maker address
+ const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({
+ senderAddress: erc721BalanceThresholdFilterInstance.address,
+ makerAddress: invalidAddress,
+ });
+ // Execute transaction
+ return expectTransactionFailedAsync(
+ erc721TakerBalanceThresholdWrapper.fillOrderAsync(signedOrderWithBadMakerAddress, validTakerAddress, {
+ takerAssetFillAmount,
+ }),
+ RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold,
+ );
+ });
+ it('should revert if taker does not meet the balance threshold', async () => {
+ return expectTransactionFailedAsync(
+ erc721NonValidBalanceThresholdWrapper.fillOrderAsync(validSignedOrder, invalidAddress, {
+ takerAssetFillAmount,
+ }),
+ RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold,
+ );
+ });
+ });
+
+ describe('fillOrderNoThrow', () => {
+ beforeEach(async () => {
+ erc20Balances = await erc20Wrapper.getBalancesAsync();
+ validSignedOrder = await orderFactory.newSignedOrderAsync();
+ });
+ it('should transfer the correct amounts and validate both maker/taker when both maker and taker meet the balance threshold', async () => {
+ // Execute a valid fill
+ const txReceipt = await erc721TakerBalanceThresholdWrapper.fillOrderNoThrowAsync(
+ validSignedOrder,
+ validTakerAddress,
+ {
+ takerAssetFillAmount,
+ // HACK(albrow): We need to hardcode the gas estimate here because
+ // the Geth gas estimator doesn't work with the way we use
+ // delegatecall and swallow errors.
+ gas: 600000,
+ },
+ );
+ // Assert validated addresses
+ const expectedValidatedAddresseses = [validSignedOrder.makerAddress, validTakerAddress];
+ await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses);
+ // Check balances
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ const makerAssetFillAmount = takerAssetFillAmount
+ .times(validSignedOrder.makerAssetAmount)
+ .dividedToIntegerBy(validSignedOrder.takerAssetAmount);
+ const makerFeePaid = validSignedOrder.makerFee
+ .times(makerAssetFillAmount)
+ .dividedToIntegerBy(validSignedOrder.makerAssetAmount);
+ const takerFeePaid = validSignedOrder.takerFee
+ .times(makerAssetFillAmount)
+ .dividedToIntegerBy(validSignedOrder.makerAssetAmount);
+ expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount),
+ );
+ expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][defaultTakerAssetAddress].plus(takerAssetFillAmount),
+ );
+ expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][zrxToken.address].minus(makerFeePaid),
+ );
+ expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount),
+ );
+ expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount),
+ );
+ expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid),
+ );
+ expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[feeRecipientAddress][zrxToken.address].plus(makerFeePaid.plus(takerFeePaid)),
+ );
+ });
+ it('should revert if maker does not meet the balance threshold', async () => {
+ // Create signed order with non-valid maker address
+ const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({
+ senderAddress: erc721BalanceThresholdFilterInstance.address,
+ makerAddress: invalidAddress,
+ });
+ // Execute transaction
+ return expectTransactionFailedAsync(
+ erc721TakerBalanceThresholdWrapper.fillOrderNoThrowAsync(
+ signedOrderWithBadMakerAddress,
+ validTakerAddress,
+ { takerAssetFillAmount },
+ ),
+ RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold,
+ );
+ });
+ it('should revert if taker does not meet the balance threshold', async () => {
+ return expectTransactionFailedAsync(
+ erc721NonValidBalanceThresholdWrapper.fillOrderNoThrowAsync(validSignedOrder, invalidAddress, {
+ takerAssetFillAmount,
+ }),
+ RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold,
+ );
+ });
+ });
+
+ describe('fillOrKillOrder', () => {
+ beforeEach(async () => {
+ erc20Balances = await erc20Wrapper.getBalancesAsync();
+ validSignedOrder = await orderFactory.newSignedOrderAsync();
+ });
+ it('should transfer the correct amounts and validate both maker/taker when both maker and taker meet the balance threshold', async () => {
+ // Execute a valid fill
+ const takerAssetFillAmount_ = validSignedOrder.takerAssetAmount;
+ const txReceipt = await erc721TakerBalanceThresholdWrapper.fillOrKillOrderAsync(
+ validSignedOrder,
+ validTakerAddress,
+ { takerAssetFillAmount: takerAssetFillAmount_ },
+ );
+ // Assert validated addresses
+ const expectedValidatedAddresseses = [validSignedOrder.makerAddress, validTakerAddress];
+ await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses);
+ // Check balances
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ const makerAssetFillAmount = takerAssetFillAmount_
+ .times(validSignedOrder.makerAssetAmount)
+ .dividedToIntegerBy(validSignedOrder.takerAssetAmount);
+ const makerFeePaid = validSignedOrder.makerFee
+ .times(makerAssetFillAmount)
+ .dividedToIntegerBy(validSignedOrder.makerAssetAmount);
+ const takerFeePaid = validSignedOrder.takerFee
+ .times(makerAssetFillAmount)
+ .dividedToIntegerBy(validSignedOrder.makerAssetAmount);
+ expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount),
+ );
+ expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][defaultTakerAssetAddress].plus(takerAssetFillAmount_),
+ );
+ expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][zrxToken.address].minus(makerFeePaid),
+ );
+ expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount_),
+ );
+ expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount),
+ );
+ expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid),
+ );
+ expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[feeRecipientAddress][zrxToken.address].plus(makerFeePaid.plus(takerFeePaid)),
+ );
+ });
+ it('should revert if maker does not meet the balance threshold', async () => {
+ // Create signed order with non-valid maker address
+ const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({
+ senderAddress: erc721BalanceThresholdFilterInstance.address,
+ makerAddress: invalidAddress,
+ });
+ // Execute transaction
+ return expectTransactionFailedAsync(
+ erc721TakerBalanceThresholdWrapper.fillOrKillOrderAsync(
+ signedOrderWithBadMakerAddress,
+ validTakerAddress,
+ { takerAssetFillAmount },
+ ),
+ RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold,
+ );
+ });
+ it('should revert if taker does not meet the balance threshold', async () => {
+ return expectTransactionFailedAsync(
+ erc721NonValidBalanceThresholdWrapper.fillOrKillOrderAsync(validSignedOrder, invalidAddress, {
+ takerAssetFillAmount,
+ }),
+ RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold,
+ );
+ });
+ it('should revert if takerAssetFillAmount is not fully filled', async () => {
+ const tooBigTakerAssetFillAmount = validSignedOrder.takerAssetAmount.times(2);
+ return expectTransactionFailedAsync(
+ erc721TakerBalanceThresholdWrapper.fillOrKillOrderAsync(validSignedOrder, validTakerAddress, {
+ takerAssetFillAmount: tooBigTakerAssetFillAmount,
+ }),
+ RevertReason.FailedExecution,
+ );
+ });
+ });
+
+ describe('marketSellOrders', () => {
+ beforeEach(async () => {
+ erc20Balances = await erc20Wrapper.getBalancesAsync();
+ validSignedOrder = await orderFactory.newSignedOrderAsync();
+ validSignedOrder2 = await orderFactory2.newSignedOrderAsync();
+ });
+ it('should transfer the correct amounts and validate both makers/taker when both makers and taker meet the balance threshold', async () => {
+ // Execute a valid fill
+ const orders = [validSignedOrder, validSignedOrder2];
+ const cumulativeTakerAssetFillAmount = validSignedOrder.takerAssetAmount.plus(takerAssetFillAmount);
+ const txReceipt = await erc721TakerBalanceThresholdWrapper.marketSellOrdersAsync(
+ orders,
+ validTakerAddress,
+ { takerAssetFillAmount: cumulativeTakerAssetFillAmount },
+ );
+ // Assert validated addresses
+ const expectedValidatedAddresseses = [
+ validSignedOrder.makerAddress,
+ validSignedOrder2.makerAddress,
+ validTakerAddress,
+ ];
+ await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses);
+ // Check balances
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ const makerAssetFillAmount2 = takerAssetFillAmount
+ .times(validSignedOrder.makerAssetAmount)
+ .dividedToIntegerBy(validSignedOrder.takerAssetAmount);
+ const makerFeePaid2 = validSignedOrder2.makerFee
+ .times(makerAssetFillAmount2)
+ .dividedToIntegerBy(validSignedOrder2.makerAssetAmount);
+ const takerFeePaid2 = validSignedOrder2.takerFee
+ .times(makerAssetFillAmount2)
+ .dividedToIntegerBy(validSignedOrder2.makerAssetAmount);
+ const takerFeePaid = validSignedOrder.takerFee.plus(takerFeePaid2);
+ const cumulativeMakerAssetFillAmount = validSignedOrder.makerAssetAmount.plus(makerAssetFillAmount2);
+ // Maker #1
+ expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(validSignedOrder.makerAssetAmount),
+ );
+ expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][defaultTakerAssetAddress].plus(validSignedOrder.takerAssetAmount),
+ );
+ expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][zrxToken.address].minus(validSignedOrder.makerFee),
+ );
+ // Maker #2
+ expect(newBalances[validMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount2),
+ );
+ expect(newBalances[validMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress2][defaultTakerAssetAddress].plus(takerAssetFillAmount),
+ );
+ expect(newBalances[validMakerAddress2][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress2][zrxToken.address].minus(makerFeePaid2),
+ );
+ // Taker
+ expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount),
+ );
+ expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][defaultMakerAssetAddress].plus(cumulativeMakerAssetFillAmount),
+ );
+ expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid),
+ );
+ // Fee recipient
+ expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[feeRecipientAddress][zrxToken.address]
+ .plus(validSignedOrder.makerFee)
+ .plus(makerFeePaid2)
+ .plus(takerFeePaid),
+ );
+ });
+ it('should revert if one maker does not meet the balance threshold', async () => {
+ // Create order set with one non-valid maker address
+ const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({
+ makerAddress: invalidAddress,
+ });
+ const orders = [validSignedOrder, signedOrderWithBadMakerAddress];
+ // Execute transaction
+ return expectTransactionFailedAsync(
+ erc721TakerBalanceThresholdWrapper.marketSellOrdersAsync(orders, validTakerAddress, {
+ takerAssetFillAmount,
+ }),
+ RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold,
+ );
+ });
+ it('should revert if taker does not meet the balance threshold', async () => {
+ const orders = [validSignedOrder, validSignedOrder2];
+ return expectTransactionFailedAsync(
+ erc721NonValidBalanceThresholdWrapper.marketSellOrdersAsync(orders, invalidAddress, {
+ takerAssetFillAmount,
+ }),
+ RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold,
+ );
+ });
+ });
+
+ describe('marketSellOrdersNoThrow', () => {
+ beforeEach(async () => {
+ erc20Balances = await erc20Wrapper.getBalancesAsync();
+ validSignedOrder = await orderFactory.newSignedOrderAsync();
+ validSignedOrder2 = await orderFactory2.newSignedOrderAsync();
+ });
+ it('should transfer the correct amounts and validate both makers/taker when both makers and taker meet the balance threshold', async () => {
+ // Execute a valid fill
+ const orders = [validSignedOrder, validSignedOrder2];
+ const cumulativeTakerAssetFillAmount = validSignedOrder.takerAssetAmount.plus(takerAssetFillAmount);
+ const txReceipt = await erc721TakerBalanceThresholdWrapper.marketSellOrdersNoThrowAsync(
+ orders,
+ validTakerAddress,
+ {
+ takerAssetFillAmount: cumulativeTakerAssetFillAmount,
+ // HACK(albrow): We need to hardcode the gas estimate here because
+ // the Geth gas estimator doesn't work with the way we use
+ // delegatecall and swallow errors.
+ gas: 600000,
+ },
+ );
+ // Assert validated addresses
+ const expectedValidatedAddresseses = [
+ validSignedOrder.makerAddress,
+ validSignedOrder2.makerAddress,
+ validTakerAddress,
+ ];
+ await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses);
+ // Check balances
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ const makerAssetFillAmount2 = takerAssetFillAmount
+ .times(validSignedOrder.makerAssetAmount)
+ .dividedToIntegerBy(validSignedOrder.takerAssetAmount);
+ const makerFeePaid2 = validSignedOrder2.makerFee
+ .times(makerAssetFillAmount2)
+ .dividedToIntegerBy(validSignedOrder2.makerAssetAmount);
+ const takerFeePaid2 = validSignedOrder2.takerFee
+ .times(makerAssetFillAmount2)
+ .dividedToIntegerBy(validSignedOrder2.makerAssetAmount);
+ const takerFeePaid = validSignedOrder.takerFee.plus(takerFeePaid2);
+ const cumulativeMakerAssetFillAmount = validSignedOrder.makerAssetAmount.plus(makerAssetFillAmount2);
+ // Maker #1
+ expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(validSignedOrder.makerAssetAmount),
+ );
+ expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][defaultTakerAssetAddress].plus(validSignedOrder.takerAssetAmount),
+ );
+ expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][zrxToken.address].minus(validSignedOrder.makerFee),
+ );
+ // Maker #2
+ expect(newBalances[validMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount2),
+ );
+ expect(newBalances[validMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress2][defaultTakerAssetAddress].plus(takerAssetFillAmount),
+ );
+ expect(newBalances[validMakerAddress2][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress2][zrxToken.address].minus(makerFeePaid2),
+ );
+ // Taker
+ expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount),
+ );
+ expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][defaultMakerAssetAddress].plus(cumulativeMakerAssetFillAmount),
+ );
+ expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid),
+ );
+ // Fee recipient
+ expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[feeRecipientAddress][zrxToken.address]
+ .plus(validSignedOrder.makerFee)
+ .plus(makerFeePaid2)
+ .plus(takerFeePaid),
+ );
+ });
+ it('should revert if one maker does not meet the balance threshold', async () => {
+ // Create order set with one non-valid maker address
+ const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({
+ makerAddress: invalidAddress,
+ });
+ const orders = [validSignedOrder, signedOrderWithBadMakerAddress];
+ // Execute transaction
+ return expectTransactionFailedAsync(
+ erc721TakerBalanceThresholdWrapper.marketSellOrdersNoThrowAsync(orders, validTakerAddress, {
+ takerAssetFillAmount,
+ }),
+ RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold,
+ );
+ });
+ it('should revert if taker does not meet the balance threshold', async () => {
+ const orders = [validSignedOrder, validSignedOrder2];
+ return expectTransactionFailedAsync(
+ erc721NonValidBalanceThresholdWrapper.marketSellOrdersNoThrowAsync(orders, invalidAddress, {
+ takerAssetFillAmount,
+ }),
+ RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold,
+ );
+ });
+ });
+
+ describe('marketBuyOrders', () => {
+ beforeEach(async () => {
+ erc20Balances = await erc20Wrapper.getBalancesAsync();
+ validSignedOrder = await orderFactory.newSignedOrderAsync();
+ validSignedOrder2 = await orderFactory2.newSignedOrderAsync();
+ });
+ it('should transfer the correct amounts and validate both makers/taker when both makers and taker meet the balance threshold', async () => {
+ // Execute a valid fill
+ const orders = [validSignedOrder, validSignedOrder2];
+ const cumulativeTakerAssetFillAmount = validSignedOrder.takerAssetAmount.plus(takerAssetFillAmount);
+ const makerAssetFillAmount2 = takerAssetFillAmount
+ .times(validSignedOrder.makerAssetAmount)
+ .dividedToIntegerBy(validSignedOrder.takerAssetAmount);
+ const cumulativeMakerAssetFillAmount = validSignedOrder.makerAssetAmount.plus(makerAssetFillAmount2);
+ const txReceipt = await erc721TakerBalanceThresholdWrapper.marketBuyOrdersAsync(orders, validTakerAddress, {
+ makerAssetFillAmount: cumulativeMakerAssetFillAmount,
+ });
+ // Assert validated addresses
+ const expectedValidatedAddresseses = [
+ validSignedOrder.makerAddress,
+ validSignedOrder2.makerAddress,
+ validTakerAddress,
+ ];
+ await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses);
+ // Check balances
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ const makerFeePaid2 = validSignedOrder2.makerFee
+ .times(makerAssetFillAmount2)
+ .dividedToIntegerBy(validSignedOrder2.makerAssetAmount);
+ const takerFeePaid2 = validSignedOrder2.takerFee
+ .times(makerAssetFillAmount2)
+ .dividedToIntegerBy(validSignedOrder2.makerAssetAmount);
+ const takerFeePaid = validSignedOrder.takerFee.plus(takerFeePaid2);
+ // Maker #1
+ expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(validSignedOrder.makerAssetAmount),
+ );
+ expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][defaultTakerAssetAddress].plus(validSignedOrder.takerAssetAmount),
+ );
+ expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][zrxToken.address].minus(validSignedOrder.makerFee),
+ );
+ // Maker #2
+ expect(newBalances[validMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount2),
+ );
+ expect(newBalances[validMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress2][defaultTakerAssetAddress].plus(takerAssetFillAmount),
+ );
+ expect(newBalances[validMakerAddress2][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress2][zrxToken.address].minus(makerFeePaid2),
+ );
+ // Taker
+ expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount),
+ );
+ expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][defaultMakerAssetAddress].plus(cumulativeMakerAssetFillAmount),
+ );
+ expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid),
+ );
+ // Fee recipient
+ expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[feeRecipientAddress][zrxToken.address]
+ .plus(validSignedOrder.makerFee)
+ .plus(makerFeePaid2)
+ .plus(takerFeePaid),
+ );
+ });
+ it('should revert if one maker does not meet the balance threshold', async () => {
+ // Create order set with one non-valid maker address
+ const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({
+ makerAddress: invalidAddress,
+ });
+ const orders = [validSignedOrder, signedOrderWithBadMakerAddress];
+ // Execute transaction
+ const dummyMakerAssetFillAmount = new BigNumber(0);
+ return expectTransactionFailedAsync(
+ erc721TakerBalanceThresholdWrapper.marketBuyOrdersAsync(orders, validTakerAddress, {
+ makerAssetFillAmount: dummyMakerAssetFillAmount,
+ }),
+ RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold,
+ );
+ });
+ it('should revert if taker does not meet the balance threshold', async () => {
+ const orders = [validSignedOrder, validSignedOrder2];
+ const dummyMakerAssetFillAmount = new BigNumber(0);
+ return expectTransactionFailedAsync(
+ erc721NonValidBalanceThresholdWrapper.marketBuyOrdersAsync(orders, invalidAddress, {
+ makerAssetFillAmount: dummyMakerAssetFillAmount,
+ }),
+ RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold,
+ );
+ });
+ });
+
+ describe('marketBuyOrdersNoThrowAsync', () => {
+ beforeEach(async () => {
+ erc20Balances = await erc20Wrapper.getBalancesAsync();
+ validSignedOrder = await orderFactory.newSignedOrderAsync();
+ validSignedOrder2 = await orderFactory2.newSignedOrderAsync();
+ });
+ it('should transfer the correct amounts and validate both makers/taker when both makers and taker meet the balance threshold', async () => {
+ // Execute a valid fill
+ const orders = [validSignedOrder, validSignedOrder2];
+ const cumulativeTakerAssetFillAmount = validSignedOrder.takerAssetAmount.plus(takerAssetFillAmount);
+ const makerAssetFillAmount2 = takerAssetFillAmount
+ .times(validSignedOrder.makerAssetAmount)
+ .dividedToIntegerBy(validSignedOrder.takerAssetAmount);
+ const cumulativeMakerAssetFillAmount = validSignedOrder.makerAssetAmount.plus(makerAssetFillAmount2);
+ const txReceipt = await erc721TakerBalanceThresholdWrapper.marketBuyOrdersNoThrowAsync(
+ orders,
+ validTakerAddress,
+ {
+ makerAssetFillAmount: cumulativeMakerAssetFillAmount,
+ // HACK(albrow): We need to hardcode the gas estimate here because
+ // the Geth gas estimator doesn't work with the way we use
+ // delegatecall and swallow errors.
+ gas: 600000,
+ },
+ );
+ // Assert validated addresses
+ const expectedValidatedAddresseses = [
+ validSignedOrder.makerAddress,
+ validSignedOrder2.makerAddress,
+ validTakerAddress,
+ ];
+ await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses);
+ // Check balances
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ const makerFeePaid2 = validSignedOrder2.makerFee
+ .times(makerAssetFillAmount2)
+ .dividedToIntegerBy(validSignedOrder2.makerAssetAmount);
+ const takerFeePaid2 = validSignedOrder2.takerFee
+ .times(makerAssetFillAmount2)
+ .dividedToIntegerBy(validSignedOrder2.makerAssetAmount);
+ const takerFeePaid = validSignedOrder.takerFee.plus(takerFeePaid2);
+ // Maker #1
+ expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(validSignedOrder.makerAssetAmount),
+ );
+ expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][defaultTakerAssetAddress].plus(validSignedOrder.takerAssetAmount),
+ );
+ expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress][zrxToken.address].minus(validSignedOrder.makerFee),
+ );
+ // Maker #2
+ expect(newBalances[validMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount2),
+ );
+ expect(newBalances[validMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress2][defaultTakerAssetAddress].plus(takerAssetFillAmount),
+ );
+ expect(newBalances[validMakerAddress2][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validMakerAddress2][zrxToken.address].minus(makerFeePaid2),
+ );
+ // Taker
+ expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount),
+ );
+ expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][defaultMakerAssetAddress].plus(cumulativeMakerAssetFillAmount),
+ );
+ expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid),
+ );
+ // Fee recipient
+ expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[feeRecipientAddress][zrxToken.address]
+ .plus(validSignedOrder.makerFee)
+ .plus(makerFeePaid2)
+ .plus(takerFeePaid),
+ );
+ });
+ it('should revert if one maker does not meet the balance threshold', async () => {
+ // Create order set with one non-valid maker address
+ const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({
+ makerAddress: invalidAddress,
+ });
+ const orders = [validSignedOrder, signedOrderWithBadMakerAddress];
+ // Execute transaction
+ const dummyMakerAssetFillAmount = new BigNumber(0);
+ return expectTransactionFailedAsync(
+ erc721TakerBalanceThresholdWrapper.marketBuyOrdersNoThrowAsync(orders, validTakerAddress, {
+ makerAssetFillAmount: dummyMakerAssetFillAmount,
+ }),
+ RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold,
+ );
+ });
+ it('should revert if taker does not meet the balance threshold', async () => {
+ const orders = [validSignedOrder, validSignedOrder2];
+ const dummyMakerAssetFillAmount = new BigNumber(0);
+ return expectTransactionFailedAsync(
+ erc721NonValidBalanceThresholdWrapper.marketBuyOrdersNoThrowAsync(orders, invalidAddress, {
+ makerAssetFillAmount: dummyMakerAssetFillAmount,
+ }),
+ RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold,
+ );
+ });
+ });
+
+ describe('matchOrders', () => {
+ beforeEach(async () => {
+ erc20Balances = await erc20Wrapper.getBalancesAsync();
+ validSignedOrder = await orderFactory.newSignedOrderAsync();
+ validSignedOrder2 = await orderFactory2.newSignedOrderAsync();
+ });
+ it('Should transfer correct amounts when both makers and taker meet the balance threshold', async () => {
+ // Test values/results taken from Match Orders test:
+ // 'Should transfer correct amounts when right order is fully filled and values pass isRoundingErrorFloor but fail isRoundingErrorCeil'
+ // Create orders to match
+ const signedOrderLeft = await orderFactory.newSignedOrderAsync({
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(17), 0),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(98), 0),
+ makerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18),
+ takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18),
+ feeRecipientAddress,
+ });
+ const signedOrderRight = await orderFactory2.newSignedOrderAsync({
+ makerAssetData: assetDataUtils.encodeERC20AssetData(defaultTakerAssetAddress),
+ takerAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(75), 0),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(13), 0),
+ makerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18),
+ takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18),
+ feeRecipientAddress,
+ });
+ // Compute expected transfer amounts
+ const expectedTransferAmounts = {
+ // Left Maker
+ amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(13), 0),
+ amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(75), 0),
+ feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber('76.4705882352941176'), 16), // 76.47%
+ // Right Maker
+ amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(75), 0),
+ amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(13), 0),
+ feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100%
+ // Taker
+ amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(0), 0),
+ feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber('76.5306122448979591'), 16), // 76.53%
+ feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100%
+ };
+ const txReceipt = await erc721TakerBalanceThresholdWrapper.matchOrdersAsync(
+ signedOrderLeft,
+ signedOrderRight,
+ validTakerAddress,
+ );
+ // Assert validated addresses
+ const expectedValidatedAddresseses = [
+ signedOrderLeft.makerAddress,
+ signedOrderRight.makerAddress,
+ validTakerAddress,
+ ];
+ await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses);
+ // Check balances
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ expect(
+ newBalances[signedOrderLeft.makerAddress][defaultMakerAssetAddress],
+ 'Checking left maker egress ERC20 account balance',
+ ).to.be.bignumber.equal(
+ erc20Balances[signedOrderLeft.makerAddress][defaultMakerAssetAddress].minus(
+ expectedTransferAmounts.amountSoldByLeftMaker,
+ ),
+ );
+ expect(
+ newBalances[signedOrderRight.makerAddress][defaultTakerAssetAddress],
+ 'Checking right maker ingress ERC20 account balance',
+ ).to.be.bignumber.equal(
+ erc20Balances[signedOrderRight.makerAddress][defaultTakerAssetAddress].minus(
+ expectedTransferAmounts.amountSoldByRightMaker,
+ ),
+ );
+ expect(
+ newBalances[validTakerAddress][defaultMakerAssetAddress],
+ 'Checking taker ingress ERC20 account balance',
+ ).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][defaultMakerAssetAddress].plus(
+ expectedTransferAmounts.amountReceivedByTaker,
+ ),
+ );
+ expect(
+ newBalances[signedOrderLeft.makerAddress][defaultTakerAssetAddress],
+ 'Checking left maker ingress ERC20 account balance',
+ ).to.be.bignumber.equal(
+ erc20Balances[signedOrderLeft.makerAddress][defaultTakerAssetAddress].plus(
+ expectedTransferAmounts.amountBoughtByLeftMaker,
+ ),
+ );
+ expect(
+ newBalances[signedOrderRight.makerAddress][defaultMakerAssetAddress],
+ 'Checking right maker egress ERC20 account balance',
+ ).to.be.bignumber.equal(
+ erc20Balances[signedOrderRight.makerAddress][defaultMakerAssetAddress].plus(
+ expectedTransferAmounts.amountBoughtByRightMaker,
+ ),
+ );
+ // Paid fees
+ expect(
+ newBalances[signedOrderLeft.makerAddress][zrxToken.address],
+ 'Checking left maker egress ERC20 account fees',
+ ).to.be.bignumber.equal(
+ erc20Balances[signedOrderLeft.makerAddress][zrxToken.address].minus(
+ expectedTransferAmounts.feePaidByLeftMaker,
+ ),
+ );
+ expect(
+ newBalances[signedOrderRight.makerAddress][zrxToken.address],
+ 'Checking right maker egress ERC20 account fees',
+ ).to.be.bignumber.equal(
+ erc20Balances[signedOrderRight.makerAddress][zrxToken.address].minus(
+ expectedTransferAmounts.feePaidByRightMaker,
+ ),
+ );
+ expect(
+ newBalances[validTakerAddress][zrxToken.address],
+ 'Checking taker egress ERC20 account fees',
+ ).to.be.bignumber.equal(
+ erc20Balances[validTakerAddress][zrxToken.address]
+ .minus(expectedTransferAmounts.feePaidByTakerLeft)
+ .minus(expectedTransferAmounts.feePaidByTakerRight),
+ );
+ // Received fees
+ expect(
+ newBalances[signedOrderLeft.feeRecipientAddress][zrxToken.address],
+ 'Checking left fee recipient ingress ERC20 account fees',
+ ).to.be.bignumber.equal(
+ erc20Balances[feeRecipientAddress][zrxToken.address]
+ .plus(expectedTransferAmounts.feePaidByLeftMaker)
+ .plus(expectedTransferAmounts.feePaidByRightMaker)
+ .plus(expectedTransferAmounts.feePaidByTakerLeft)
+ .plus(expectedTransferAmounts.feePaidByTakerRight),
+ );
+ });
+ it('should revert if left maker does not meet the balance threshold', async () => {
+ // Create signed order with non-valid maker address
+ const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({
+ senderAddress: erc721BalanceThresholdFilterInstance.address,
+ makerAddress: invalidAddress,
+ });
+ // Execute transaction
+ return expectTransactionFailedAsync(
+ erc721TakerBalanceThresholdWrapper.matchOrdersAsync(
+ validSignedOrder,
+ signedOrderWithBadMakerAddress,
+ validTakerAddress,
+ ),
+ RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold,
+ );
+ });
+ it('should revert if right maker does not meet the balance threshold', async () => {
+ // Create signed order with non-valid maker address
+ const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({
+ senderAddress: erc721BalanceThresholdFilterInstance.address,
+ makerAddress: invalidAddress,
+ });
+ // Execute transaction
+ return expectTransactionFailedAsync(
+ erc721TakerBalanceThresholdWrapper.matchOrdersAsync(
+ signedOrderWithBadMakerAddress,
+ validSignedOrder,
+ validTakerAddress,
+ ),
+ RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold,
+ );
+ });
+ it('should revert if taker does not meet the balance threshold', async () => {
+ return expectTransactionFailedAsync(
+ erc721NonValidBalanceThresholdWrapper.matchOrdersAsync(
+ validSignedOrder,
+ validSignedOrder,
+ invalidAddress,
+ ),
+ RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold,
+ );
+ });
+ });
+
+ describe('cancelOrder', () => {
+ beforeEach(async () => {
+ erc20Balances = await erc20Wrapper.getBalancesAsync();
+ validSignedOrder = await orderFactory.newSignedOrderAsync();
+ validSignedOrder2 = await orderFactory2.newSignedOrderAsync();
+ });
+ it('Should successfully cancel order if maker meets balance threshold', async () => {
+ // Verify order is not cancelled
+ const orderInfoBeforeCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync(
+ validSignedOrder,
+ );
+ expect(orderInfoBeforeCancelling.orderStatus).to.be.equal(OrderStatus.Fillable);
+ // Cancel
+ const txReceipt = await erc721MakerBalanceThresholdWrapper.cancelOrderAsync(
+ validSignedOrder,
+ validSignedOrder.makerAddress,
+ );
+ // Assert validated addresses
+ const expectedValidatedAddresseses: string[] = [];
+ await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses);
+ // Check that order was cancelled
+ const orderInfoAfterCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync(
+ validSignedOrder,
+ );
+ expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.Cancelled);
+ });
+ it('Should successfully cancel order if maker does not meet balance threshold', async () => {
+ // Create order where maker does not meet balance threshold
+ const signedOrderWithBadMakerAddress = await invalidOrderFactory.newSignedOrderAsync({});
+ // Verify order is not cancelled
+ const orderInfoBeforeCancelling = await erc721NonValidBalanceThresholdWrapper.getOrderInfoAsync(
+ signedOrderWithBadMakerAddress,
+ );
+ expect(orderInfoBeforeCancelling.orderStatus).to.be.equal(OrderStatus.Fillable);
+ // Cancel
+ const txReceipt = await erc721NonValidBalanceThresholdWrapper.cancelOrderAsync(
+ signedOrderWithBadMakerAddress,
+ signedOrderWithBadMakerAddress.makerAddress,
+ );
+ // Assert validated addresses
+ const expectedValidatedAddresseses: string[] = [];
+ await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses);
+ // Check that order was cancelled
+ const orderInfoAfterCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync(
+ signedOrderWithBadMakerAddress,
+ );
+ expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.Cancelled);
+ });
+ });
+
+ describe('batchCancelOrders', () => {
+ beforeEach(async () => {
+ erc20Balances = await erc20Wrapper.getBalancesAsync();
+ });
+ it('Should successfully batch cancel orders if maker meets balance threshold', async () => {
+ // Create orders to cancel
+ const validSignedOrders = [
+ await orderFactory.newSignedOrderAsync(),
+ await orderFactory.newSignedOrderAsync(),
+ await orderFactory.newSignedOrderAsync(),
+ ];
+ // Verify orders are not cancelled
+ _.each(validSignedOrders, async signedOrder => {
+ const orderInfoBeforeCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync(
+ signedOrder,
+ );
+ return expect(orderInfoBeforeCancelling.orderStatus).to.be.equal(OrderStatus.Fillable);
+ });
+ // Cancel
+ const txReceipt = await erc721MakerBalanceThresholdWrapper.batchCancelOrdersAsync(
+ validSignedOrders,
+ validSignedOrders[0].makerAddress,
+ );
+ // Assert validated addresses
+ const expectedValidatedAddresseses: string[] = [];
+ await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses);
+ // Check that order was cancelled
+ _.each(validSignedOrders, async signedOrder => {
+ const orderInfoAfterCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync(
+ signedOrder,
+ );
+ return expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.Cancelled);
+ });
+ });
+ it('Should successfully batch cancel order if maker does not meet balance threshold', async () => {
+ // Create orders to cancel
+ const invalidSignedOrders = [
+ await invalidOrderFactory.newSignedOrderAsync(),
+ await invalidOrderFactory.newSignedOrderAsync(),
+ await invalidOrderFactory.newSignedOrderAsync(),
+ ];
+ // Verify orders are not cancelled
+ _.each(invalidSignedOrders, async signedOrder => {
+ const orderInfoBeforeCancelling = await erc721NonValidBalanceThresholdWrapper.getOrderInfoAsync(
+ signedOrder,
+ );
+ return expect(orderInfoBeforeCancelling.orderStatus).to.be.equal(OrderStatus.Fillable);
+ });
+ // Cancel
+ const txReceipt = await erc721NonValidBalanceThresholdWrapper.batchCancelOrdersAsync(
+ invalidSignedOrders,
+ invalidAddress,
+ );
+ // Assert validated addresses
+ const expectedValidatedAddresseses: string[] = [];
+ await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses);
+ // Check that order was cancelled
+ _.each(invalidSignedOrders, async signedOrder => {
+ const orderInfoAfterCancelling = await erc721NonValidBalanceThresholdWrapper.getOrderInfoAsync(
+ signedOrder,
+ );
+ return expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.Cancelled);
+ });
+ });
+ });
+
+ describe('cancelOrdersUpTo', () => {
+ beforeEach(async () => {
+ erc20Balances = await erc20Wrapper.getBalancesAsync();
+ });
+ it('Should successfully batch cancel orders if maker meets balance threshold', async () => {
+ // Create orders to cancel
+ const validSignedOrders = [
+ await orderFactory.newSignedOrderAsync({ salt: new BigNumber(0) }),
+ await orderFactory.newSignedOrderAsync({ salt: new BigNumber(1) }),
+ await orderFactory.newSignedOrderAsync({ salt: new BigNumber(2) }),
+ ];
+ // Verify orders are not cancelled
+ _.each(validSignedOrders, async signedOrder => {
+ const orderInfoBeforeCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync(
+ signedOrder,
+ );
+ return expect(orderInfoBeforeCancelling.orderStatus).to.be.equal(OrderStatus.Fillable);
+ });
+ // Cancel
+ const cancelOrdersUpToThisSalt = new BigNumber(1);
+ const txReceipt = await erc721MakerBalanceThresholdWrapper.cancelOrdersUpToAsync(
+ cancelOrdersUpToThisSalt,
+ validSignedOrders[0].makerAddress,
+ );
+ // Assert validated addresses
+ const expectedValidatedAddresseses: string[] = [];
+ await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses);
+ // Check that order was cancelled
+ _.each(validSignedOrders, async (signedOrder, salt: number) => {
+ const orderInfoAfterCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync(
+ signedOrder,
+ );
+ const saltAsBigNumber = new BigNumber(salt);
+ if (saltAsBigNumber.isLessThanOrEqualTo(cancelOrdersUpToThisSalt)) {
+ return expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.Cancelled);
+ } else {
+ return expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.Fillable);
+ }
+ });
+ });
+ it('Should successfully batch cancel order if maker does not meet balance threshold', async () => {
+ // Create orders to cancel
+ const invalidSignedOrders = [
+ await invalidOrderFactory.newSignedOrderAsync({ salt: new BigNumber(0) }),
+ await invalidOrderFactory.newSignedOrderAsync({ salt: new BigNumber(1) }),
+ await invalidOrderFactory.newSignedOrderAsync({ salt: new BigNumber(2) }),
+ ];
+ // Verify orders are not cancelled
+ _.each(invalidSignedOrders, async signedOrder => {
+ const orderInfoBeforeCancelling = await erc721NonValidBalanceThresholdWrapper.getOrderInfoAsync(
+ signedOrder,
+ );
+ return expect(orderInfoBeforeCancelling.orderStatus).to.be.equal(OrderStatus.Fillable);
+ });
+ // Cancel
+ const cancelOrdersUpToThisSalt = new BigNumber(1);
+ const txReceipt = await erc721NonValidBalanceThresholdWrapper.cancelOrdersUpToAsync(
+ cancelOrdersUpToThisSalt,
+ invalidAddress,
+ );
+ // Assert validated addresses
+ const expectedValidatedAddresseses: string[] = [];
+ await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses);
+ // Check that order was cancelled
+ _.each(invalidSignedOrders, async (signedOrder, salt: number) => {
+ const orderInfoAfterCancelling = await erc721NonValidBalanceThresholdWrapper.getOrderInfoAsync(
+ signedOrder,
+ );
+ const saltAsBigNumber = new BigNumber(salt);
+ if (saltAsBigNumber.isLessThanOrEqualTo(cancelOrdersUpToThisSalt)) {
+ return expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.Cancelled);
+ } else {
+ return expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.Fillable);
+ }
+ });
+ });
+ });
+});
+// tslint:disable:max-file-line-count
+// tslint:enable:no-unnecessary-type-assertion