aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contracts/test
diff options
context:
space:
mode:
Diffstat (limited to 'packages/contracts/test')
-rw-r--r--packages/contracts/test/asset_proxy/proxies.ts16
-rw-r--r--packages/contracts/test/exchange/core.ts12
-rw-r--r--packages/contracts/test/exchange/dispatcher.ts10
-rw-r--r--packages/contracts/test/exchange/match_orders.ts8
-rw-r--r--packages/contracts/test/exchange/signature_validator.ts6
-rw-r--r--packages/contracts/test/exchange/transactions.ts4
-rw-r--r--packages/contracts/test/exchange/wrapper.ts8
-rw-r--r--packages/contracts/test/forwarder/forwarder.ts819
-rw-r--r--packages/contracts/test/multisig/asset_proxy_owner.ts44
-rw-r--r--packages/contracts/test/multisig/multi_sig_with_time_lock.ts10
-rw-r--r--packages/contracts/test/tokens/unlimited_allowance_token.ts2
-rw-r--r--packages/contracts/test/utils/artifacts.ts2
-rw-r--r--packages/contracts/test/utils/core_combinatorial_utils.ts4
-rw-r--r--packages/contracts/test/utils/erc20_wrapper.ts12
-rw-r--r--packages/contracts/test/utils/erc721_wrapper.ts4
-rw-r--r--packages/contracts/test/utils/forwarder_wrapper.ts220
-rw-r--r--packages/contracts/test/utils/order_factory_from_scenario.ts2
-rw-r--r--packages/contracts/test/utils/types.ts8
18 files changed, 1125 insertions, 66 deletions
diff --git a/packages/contracts/test/asset_proxy/proxies.ts b/packages/contracts/test/asset_proxy/proxies.ts
index bf9f9bc3e..e1167b156 100644
--- a/packages/contracts/test/asset_proxy/proxies.ts
+++ b/packages/contracts/test/asset_proxy/proxies.ts
@@ -7,14 +7,14 @@ import { LogWithDecodedArgs } from 'ethereum-types';
import ethUtil = require('ethereumjs-util');
import * as _ from 'lodash';
-import { DummyERC20TokenContract } from '../../generated_contract_wrappers/dummy_e_r_c20_token';
+import { DummyERC20TokenContract } from '../../generated_contract_wrappers/dummy_erc20_token';
import {
DummyERC721ReceiverContract,
- TokenReceivedContractEventArgs,
-} from '../../generated_contract_wrappers/dummy_e_r_c721_receiver';
-import { DummyERC721TokenContract } from '../../generated_contract_wrappers/dummy_e_r_c721_token';
-import { ERC20ProxyContract } from '../../generated_contract_wrappers/e_r_c20_proxy';
-import { ERC721ProxyContract } from '../../generated_contract_wrappers/e_r_c721_proxy';
+ DummyERC721ReceiverTokenReceivedEventArgs,
+} from '../../generated_contract_wrappers/dummy_erc721_receiver';
+import { DummyERC721TokenContract } from '../../generated_contract_wrappers/dummy_erc721_token';
+import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_proxy';
+import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy';
import { IAssetProxyContract } from '../../generated_contract_wrappers/i_asset_proxy';
import { artifacts } from '../utils/artifacts';
import { expectTransactionFailedAsync } from '../utils/assertions';
@@ -277,7 +277,7 @@ describe('Asset Transfer Proxies', () => {
);
// Verify that no log was emitted by erc721 receiver
expect(tx.logs.length).to.be.equal(1);
- const tokenReceivedLog = tx.logs[0] as LogWithDecodedArgs<TokenReceivedContractEventArgs>;
+ const tokenReceivedLog = tx.logs[0] as LogWithDecodedArgs<DummyERC721ReceiverTokenReceivedEventArgs>;
expect(tokenReceivedLog.args.from).to.be.equal(makerAddress);
expect(tokenReceivedLog.args.tokenId).to.be.bignumber.equal(erc721MakerTokenId);
expect(tokenReceivedLog.args.data).to.be.equal(constants.NULL_BYTES);
@@ -316,7 +316,7 @@ describe('Asset Transfer Proxies', () => {
);
// Validate log emitted by erc721 receiver
expect(tx.logs.length).to.be.equal(1);
- const tokenReceivedLog = tx.logs[0] as LogWithDecodedArgs<TokenReceivedContractEventArgs>;
+ const tokenReceivedLog = tx.logs[0] as LogWithDecodedArgs<DummyERC721ReceiverTokenReceivedEventArgs>;
expect(tokenReceivedLog.args.from).to.be.equal(makerAddress);
expect(tokenReceivedLog.args.tokenId).to.be.bignumber.equal(erc721MakerTokenId);
expect(tokenReceivedLog.args.data).to.be.equal(receiverData);
diff --git a/packages/contracts/test/exchange/core.ts b/packages/contracts/test/exchange/core.ts
index 4e70893fc..d9f3851d1 100644
--- a/packages/contracts/test/exchange/core.ts
+++ b/packages/contracts/test/exchange/core.ts
@@ -8,11 +8,11 @@ import { LogWithDecodedArgs } from 'ethereum-types';
import ethUtil = require('ethereumjs-util');
import * as _ from 'lodash';
-import { DummyERC20TokenContract } from '../../generated_contract_wrappers/dummy_e_r_c20_token';
-import { DummyERC721TokenContract } from '../../generated_contract_wrappers/dummy_e_r_c721_token';
-import { ERC20ProxyContract } from '../../generated_contract_wrappers/e_r_c20_proxy';
-import { ERC721ProxyContract } from '../../generated_contract_wrappers/e_r_c721_proxy';
-import { CancelContractEventArgs, ExchangeContract } from '../../generated_contract_wrappers/exchange';
+import { DummyERC20TokenContract } from '../../generated_contract_wrappers/dummy_erc20_token';
+import { DummyERC721TokenContract } from '../../generated_contract_wrappers/dummy_erc721_token';
+import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_proxy';
+import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy';
+import { ExchangeCancelEventArgs, ExchangeContract } from '../../generated_contract_wrappers/exchange';
import { artifacts } from '../utils/artifacts';
import { expectTransactionFailedAsync } from '../utils/assertions';
import { chaiSetup } from '../utils/chai_setup';
@@ -209,7 +209,7 @@ describe('Exchange core', () => {
const res = await exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress);
expect(res.logs).to.have.length(1);
- const log = res.logs[0] as LogWithDecodedArgs<CancelContractEventArgs>;
+ const log = res.logs[0] as LogWithDecodedArgs<ExchangeCancelEventArgs>;
const logArgs = log.args;
expect(signedOrder.makerAddress).to.be.equal(logArgs.makerAddress);
diff --git a/packages/contracts/test/exchange/dispatcher.ts b/packages/contracts/test/exchange/dispatcher.ts
index 94ada1ef2..11f74d776 100644
--- a/packages/contracts/test/exchange/dispatcher.ts
+++ b/packages/contracts/test/exchange/dispatcher.ts
@@ -6,11 +6,11 @@ import * as chai from 'chai';
import { LogWithDecodedArgs } from 'ethereum-types';
import * as _ from 'lodash';
-import { DummyERC20TokenContract } from '../../generated_contract_wrappers/dummy_e_r_c20_token';
-import { ERC20ProxyContract } from '../../generated_contract_wrappers/e_r_c20_proxy';
-import { ERC721ProxyContract } from '../../generated_contract_wrappers/e_r_c721_proxy';
+import { DummyERC20TokenContract } from '../../generated_contract_wrappers/dummy_erc20_token';
+import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_proxy';
+import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy';
import {
- AssetProxyRegisteredContractEventArgs,
+ TestAssetProxyDispatcherAssetProxyRegisteredEventArgs,
TestAssetProxyDispatcherContract,
} from '../../generated_contract_wrappers/test_asset_proxy_dispatcher';
import { artifacts } from '../utils/artifacts';
@@ -150,7 +150,7 @@ describe('AssetProxyDispatcher', () => {
await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { from: owner }),
);
const logs = txReceipt.logs;
- const log = logs[0] as LogWithDecodedArgs<AssetProxyRegisteredContractEventArgs>;
+ const log = logs[0] as LogWithDecodedArgs<TestAssetProxyDispatcherAssetProxyRegisteredEventArgs>;
expect(log.args.id).to.equal(AssetProxyId.ERC20);
expect(log.args.assetProxy).to.equal(erc20Proxy.address);
});
diff --git a/packages/contracts/test/exchange/match_orders.ts b/packages/contracts/test/exchange/match_orders.ts
index 90406415f..16041e968 100644
--- a/packages/contracts/test/exchange/match_orders.ts
+++ b/packages/contracts/test/exchange/match_orders.ts
@@ -6,10 +6,10 @@ import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as chai from 'chai';
import * as _ from 'lodash';
-import { DummyERC20TokenContract } from '../../generated_contract_wrappers/dummy_e_r_c20_token';
-import { DummyERC721TokenContract } from '../../generated_contract_wrappers/dummy_e_r_c721_token';
-import { ERC20ProxyContract } from '../../generated_contract_wrappers/e_r_c20_proxy';
-import { ERC721ProxyContract } from '../../generated_contract_wrappers/e_r_c721_proxy';
+import { DummyERC20TokenContract } from '../../generated_contract_wrappers/dummy_erc20_token';
+import { DummyERC721TokenContract } from '../../generated_contract_wrappers/dummy_erc721_token';
+import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_proxy';
+import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy';
import { ExchangeContract } from '../../generated_contract_wrappers/exchange';
import { artifacts } from '../utils/artifacts';
import { expectTransactionFailedAsync } from '../utils/assertions';
diff --git a/packages/contracts/test/exchange/signature_validator.ts b/packages/contracts/test/exchange/signature_validator.ts
index c44d22479..8cd6409a5 100644
--- a/packages/contracts/test/exchange/signature_validator.ts
+++ b/packages/contracts/test/exchange/signature_validator.ts
@@ -6,8 +6,8 @@ import { LogWithDecodedArgs } from 'ethereum-types';
import ethUtil = require('ethereumjs-util');
import {
- SignatureValidatorApprovalContractEventArgs,
TestSignatureValidatorContract,
+ TestSignatureValidatorSignatureValidatorApprovalEventArgs,
} from '../../generated_contract_wrappers/test_signature_validator';
import { TestValidatorContract } from '../../generated_contract_wrappers/test_validator';
import { TestWalletContract } from '../../generated_contract_wrappers/test_wallet';
@@ -477,7 +477,7 @@ describe('MixinSignatureValidator', () => {
),
);
expect(res.logs.length).to.equal(1);
- const log = res.logs[0] as LogWithDecodedArgs<SignatureValidatorApprovalContractEventArgs>;
+ const log = res.logs[0] as LogWithDecodedArgs<TestSignatureValidatorSignatureValidatorApprovalEventArgs>;
const logArgs = log.args;
expect(logArgs.signerAddress).to.equal(signerAddress);
expect(logArgs.validatorAddress).to.equal(testValidator.address);
@@ -495,7 +495,7 @@ describe('MixinSignatureValidator', () => {
),
);
expect(res.logs.length).to.equal(1);
- const log = res.logs[0] as LogWithDecodedArgs<SignatureValidatorApprovalContractEventArgs>;
+ const log = res.logs[0] as LogWithDecodedArgs<TestSignatureValidatorSignatureValidatorApprovalEventArgs>;
const logArgs = log.args;
expect(logArgs.signerAddress).to.equal(signerAddress);
expect(logArgs.validatorAddress).to.equal(testValidator.address);
diff --git a/packages/contracts/test/exchange/transactions.ts b/packages/contracts/test/exchange/transactions.ts
index 959d79517..0d66b11b8 100644
--- a/packages/contracts/test/exchange/transactions.ts
+++ b/packages/contracts/test/exchange/transactions.ts
@@ -5,8 +5,8 @@ import { BigNumber } from '@0xproject/utils';
import * as chai from 'chai';
import * as _ from 'lodash';
-import { DummyERC20TokenContract } from '../../generated_contract_wrappers/dummy_e_r_c20_token';
-import { ERC20ProxyContract } from '../../generated_contract_wrappers/e_r_c20_proxy';
+import { DummyERC20TokenContract } from '../../generated_contract_wrappers/dummy_erc20_token';
+import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_proxy';
import { ExchangeContract } from '../../generated_contract_wrappers/exchange';
import { ExchangeWrapperContract } from '../../generated_contract_wrappers/exchange_wrapper';
import { WhitelistContract } from '../../generated_contract_wrappers/whitelist';
diff --git a/packages/contracts/test/exchange/wrapper.ts b/packages/contracts/test/exchange/wrapper.ts
index 69f374e46..655d55b83 100644
--- a/packages/contracts/test/exchange/wrapper.ts
+++ b/packages/contracts/test/exchange/wrapper.ts
@@ -6,10 +6,10 @@ import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as chai from 'chai';
import * as _ from 'lodash';
-import { DummyERC20TokenContract } from '../../generated_contract_wrappers/dummy_e_r_c20_token';
-import { DummyERC721TokenContract } from '../../generated_contract_wrappers/dummy_e_r_c721_token';
-import { ERC20ProxyContract } from '../../generated_contract_wrappers/e_r_c20_proxy';
-import { ERC721ProxyContract } from '../../generated_contract_wrappers/e_r_c721_proxy';
+import { DummyERC20TokenContract } from '../../generated_contract_wrappers/dummy_erc20_token';
+import { DummyERC721TokenContract } from '../../generated_contract_wrappers/dummy_erc721_token';
+import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_proxy';
+import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy';
import { ExchangeContract } from '../../generated_contract_wrappers/exchange';
import { artifacts } from '../utils/artifacts';
import { expectTransactionFailedAsync } from '../utils/assertions';
diff --git a/packages/contracts/test/forwarder/forwarder.ts b/packages/contracts/test/forwarder/forwarder.ts
new file mode 100644
index 000000000..b4555d417
--- /dev/null
+++ b/packages/contracts/test/forwarder/forwarder.ts
@@ -0,0 +1,819 @@
+import { BlockchainLifecycle } from '@0xproject/dev-utils';
+import { assetProxyUtils } from '@0xproject/order-utils';
+import { AssetProxyId, RevertReason, SignedOrder } from '@0xproject/types';
+import { BigNumber } from '@0xproject/utils';
+import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import * as chai from 'chai';
+import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
+
+import { DummyERC20TokenContract } from '../../generated_contract_wrappers/dummy_erc20_token';
+import { DummyERC721TokenContract } from '../../generated_contract_wrappers/dummy_erc721_token';
+import { ExchangeContract } from '../../generated_contract_wrappers/exchange';
+import { ForwarderContract } from '../../generated_contract_wrappers/forwarder';
+import { WETH9Contract } from '../../generated_contract_wrappers/weth9';
+import { artifacts } from '../utils/artifacts';
+import { expectTransactionFailedAsync } from '../utils/assertions';
+import { chaiSetup } from '../utils/chai_setup';
+import { constants } from '../utils/constants';
+import { ERC20Wrapper } from '../utils/erc20_wrapper';
+import { ERC721Wrapper } from '../utils/erc721_wrapper';
+import { ExchangeWrapper } from '../utils/exchange_wrapper';
+import { formatters } from '../utils/formatters';
+import { ForwarderWrapper } from '../utils/forwarder_wrapper';
+import { OrderFactory } from '../utils/order_factory';
+import { ContractName, ERC20BalancesByOwner } from '../utils/types';
+import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
+
+chaiSetup.configure();
+const expect = chai.expect;
+const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
+const DECIMALS_DEFAULT = 18;
+// Set a gasPrice so when checking balance of msg.sender we can accurately calculate gasPrice*gasUsed
+const DEFAULT_GAS_PRICE = new BigNumber(1);
+
+describe(ContractName.Forwarder, () => {
+ let makerAddress: string;
+ let owner: string;
+ let takerAddress: string;
+ let feeRecipientAddress: string;
+ let otherAddress: string;
+ let defaultMakerAssetAddress: string;
+
+ let weth: DummyERC20TokenContract;
+ let zrxToken: DummyERC20TokenContract;
+ let erc721Token: DummyERC721TokenContract;
+ let forwarderContract: ForwarderContract;
+ let wethContract: WETH9Contract;
+ let forwarderWrapper: ForwarderWrapper;
+ let exchangeWrapper: ExchangeWrapper;
+
+ let signedOrder: SignedOrder;
+ let signedOrders: SignedOrder[];
+ let orderWithFee: SignedOrder;
+ let signedOrdersWithFee: SignedOrder[];
+ let feeOrder: SignedOrder;
+ let feeOrders: SignedOrder[];
+ let orderFactory: OrderFactory;
+ let erc20Wrapper: ERC20Wrapper;
+ let erc20Balances: ERC20BalancesByOwner;
+ let tx: TransactionReceiptWithDecodedLogs;
+
+ let erc721MakerAssetIds: BigNumber[];
+ let feeProportion: number = 0;
+
+ before(async () => {
+ await blockchainLifecycle.startAsync();
+ const accounts = await web3Wrapper.getAvailableAddressesAsync();
+ const usedAddresses = ([owner, makerAddress, takerAddress, feeRecipientAddress, otherAddress] = accounts);
+
+ const erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner);
+ erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
+
+ const numDummyErc20ToDeploy = 3;
+ let erc20TokenA;
+ [erc20TokenA, zrxToken] = await erc20Wrapper.deployDummyTokensAsync(
+ numDummyErc20ToDeploy,
+ constants.DUMMY_TOKEN_DECIMALS,
+ );
+ const erc20Proxy = await erc20Wrapper.deployProxyAsync();
+ await erc20Wrapper.setBalancesAndAllowancesAsync();
+
+ [erc721Token] = await erc721Wrapper.deployDummyTokensAsync();
+ const erc721Proxy = await erc721Wrapper.deployProxyAsync();
+ await erc721Wrapper.setBalancesAndAllowancesAsync();
+ const erc721Balances = await erc721Wrapper.getBalancesAsync();
+ erc721MakerAssetIds = erc721Balances[makerAddress][erc721Token.address];
+
+ wethContract = await WETH9Contract.deployFrom0xArtifactAsync(artifacts.EtherToken, provider, txDefaults);
+ weth = new DummyERC20TokenContract(wethContract.abi, wethContract.address, provider);
+ erc20Wrapper.addDummyTokenContract(weth);
+
+ const wethAssetData = assetProxyUtils.encodeERC20AssetData(wethContract.address);
+ const zrxAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address);
+ const exchangeInstance = await ExchangeContract.deployFrom0xArtifactAsync(
+ artifacts.Exchange,
+ provider,
+ txDefaults,
+ zrxAssetData,
+ );
+ const exchangeContract = new ExchangeContract(exchangeInstance.abi, exchangeInstance.address, provider);
+ exchangeWrapper = new ExchangeWrapper(exchangeContract, provider);
+ await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner);
+ await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner);
+
+ await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeInstance.address, {
+ from: owner,
+ });
+ await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeInstance.address, {
+ from: owner,
+ });
+
+ defaultMakerAssetAddress = erc20TokenA.address;
+ const defaultTakerAssetAddress = wethContract.address;
+ const defaultOrderParams = {
+ exchangeAddress: exchangeInstance.address,
+ makerAddress,
+ feeRecipientAddress,
+ makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultMakerAssetAddress),
+ takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultTakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), DECIMALS_DEFAULT),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), DECIMALS_DEFAULT),
+ makerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT),
+ takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(0), DECIMALS_DEFAULT),
+ };
+ const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
+ orderFactory = new OrderFactory(privateKey, defaultOrderParams);
+
+ const forwarderInstance = await ForwarderContract.deployFrom0xArtifactAsync(
+ artifacts.Forwarder,
+ provider,
+ txDefaults,
+ exchangeInstance.address,
+ wethContract.address,
+ zrxToken.address,
+ AssetProxyId.ERC20,
+ zrxAssetData,
+ wethAssetData,
+ );
+ forwarderContract = new ForwarderContract(forwarderInstance.abi, forwarderInstance.address, provider);
+ forwarderWrapper = new ForwarderWrapper(forwarderContract, provider, zrxToken.address);
+ erc20Wrapper.addTokenOwnerAddress(forwarderInstance.address);
+
+ web3Wrapper.abiDecoder.addABI(forwarderContract.abi);
+ web3Wrapper.abiDecoder.addABI(exchangeInstance.abi);
+ });
+ after(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ feeProportion = 0;
+ erc20Balances = await erc20Wrapper.getBalancesAsync();
+ signedOrder = orderFactory.newSignedOrder();
+ signedOrders = [signedOrder];
+ feeOrder = orderFactory.newSignedOrder({
+ makerAssetData: assetProxyUtils.encodeERC20AssetData(zrxToken.address),
+ takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT),
+ });
+ feeOrders = [feeOrder];
+ orderWithFee = orderFactory.newSignedOrder({
+ takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT),
+ });
+ signedOrdersWithFee = [orderWithFee];
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ describe('calculations', () => {
+ it('throws if partially filled orders passed in are not enough to satisfy requested amount', async () => {
+ feeOrders = [feeOrder];
+ const makerTokenFillAmount = feeOrder.makerAssetAmount.div(2);
+ const fillAmountWei = await forwarderWrapper.calculateMarketBuyFillAmountWeiAsync(
+ feeOrders,
+ [],
+ feeProportion,
+ makerTokenFillAmount,
+ );
+ // Fill the feeOrder
+ tx = await forwarderWrapper.marketBuyTokensWithEthAsync(feeOrders, [], makerTokenFillAmount, {
+ from: takerAddress,
+ value: fillAmountWei,
+ });
+ return expect(
+ forwarderWrapper.calculateMarketBuyFillAmountWeiAsync(
+ feeOrders,
+ [],
+ feeProportion,
+ makerTokenFillAmount,
+ ),
+ ).to.be.rejectedWith('Unable to satisfy makerAssetFillAmount with provided orders');
+ });
+ it('throws if orders passed are cancelled', async () => {
+ tx = await exchangeWrapper.cancelOrderAsync(feeOrder, makerAddress);
+ // Cancel the feeOrder
+ return expect(
+ forwarderWrapper.calculateMarketBuyFillAmountWeiAsync(
+ feeOrders,
+ [],
+ feeProportion,
+ feeOrder.makerAssetAmount.div(2),
+ ),
+ ).to.be.rejectedWith('Unable to satisfy makerAssetFillAmount with provided orders');
+ });
+ });
+ describe('marketSellEthForERC20 without extra fees', () => {
+ it('should fill the order', async () => {
+ const fillAmount = signedOrder.takerAssetAmount.div(2);
+ const makerBalanceBefore = erc20Balances[makerAddress][defaultMakerAssetAddress];
+ const takerBalanceBefore = erc20Balances[takerAddress][defaultMakerAssetAddress];
+ feeOrders = [];
+ tx = await forwarderWrapper.marketSellEthForERC20Async(signedOrders, feeOrders, {
+ value: fillAmount,
+ from: takerAddress,
+ });
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ const makerBalanceAfter = newBalances[makerAddress][defaultMakerAssetAddress];
+ const takerBalanceAfter = newBalances[takerAddress][defaultMakerAssetAddress];
+ const makerTokenFillAmount = fillAmount
+ .times(signedOrder.makerAssetAmount)
+ .dividedToIntegerBy(signedOrder.takerAssetAmount);
+
+ expect(makerBalanceAfter).to.be.bignumber.equal(makerBalanceBefore.minus(makerTokenFillAmount));
+ expect(takerBalanceAfter).to.be.bignumber.equal(takerBalanceBefore.plus(makerTokenFillAmount));
+ expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(new BigNumber(0));
+ });
+ it('should fill the order and perform fee abstraction', async () => {
+ const fillAmount = signedOrder.takerAssetAmount.div(4);
+ const takerBalanceBefore = erc20Balances[takerAddress][defaultMakerAssetAddress];
+ tx = await forwarderWrapper.marketSellEthForERC20Async(signedOrdersWithFee, feeOrders, {
+ value: fillAmount,
+ from: takerAddress,
+ });
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ const takerBalanceAfter = newBalances[takerAddress][defaultMakerAssetAddress];
+
+ const acceptPercentage = 98;
+ const acceptableThreshold = takerBalanceBefore.plus(fillAmount.times(acceptPercentage).dividedBy(100));
+ const isWithinThreshold = takerBalanceAfter.greaterThanOrEqualTo(acceptableThreshold);
+ expect(isWithinThreshold).to.be.true();
+ expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(new BigNumber(0));
+ });
+ it('should fill the order when token is ZRX with fees', async () => {
+ orderWithFee = orderFactory.newSignedOrder({
+ makerAssetData: assetProxyUtils.encodeERC20AssetData(zrxToken.address),
+ takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT),
+ });
+ signedOrdersWithFee = [orderWithFee];
+ feeOrders = [];
+ const fillAmount = signedOrder.takerAssetAmount.div(4);
+ const takerBalanceBefore = erc20Balances[takerAddress][zrxToken.address];
+ tx = await forwarderWrapper.marketSellEthForERC20Async(signedOrdersWithFee, feeOrders, {
+ value: fillAmount,
+ from: takerAddress,
+ });
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ const takerBalanceAfter = newBalances[takerAddress][zrxToken.address];
+
+ const acceptPercentage = 98;
+ const acceptableThreshold = takerBalanceBefore.plus(fillAmount.times(acceptPercentage).dividedBy(100));
+ const isWithinThreshold = takerBalanceAfter.greaterThanOrEqualTo(acceptableThreshold);
+ expect(isWithinThreshold).to.be.true();
+ expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(new BigNumber(0));
+ });
+ it('should fail if sent an ETH amount too high', async () => {
+ signedOrder = orderFactory.newSignedOrder({
+ makerAssetData: assetProxyUtils.encodeERC20AssetData(zrxToken.address),
+ takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT),
+ });
+ const fillAmount = signedOrder.takerAssetAmount.times(2);
+ return expectTransactionFailedAsync(
+ forwarderWrapper.marketSellEthForERC20Async(signedOrdersWithFee, feeOrders, {
+ value: fillAmount,
+ from: takerAddress,
+ }),
+ RevertReason.UnacceptableThreshold,
+ );
+ });
+ it('should fail if fee abstraction amount is too high', async () => {
+ orderWithFee = orderFactory.newSignedOrder({
+ takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), DECIMALS_DEFAULT),
+ });
+ signedOrdersWithFee = [orderWithFee];
+ feeOrder = orderFactory.newSignedOrder({
+ makerAssetData: assetProxyUtils.encodeERC20AssetData(zrxToken.address),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT),
+ });
+ feeOrders = [feeOrder];
+ const fillAmount = signedOrder.takerAssetAmount.div(4);
+ return expectTransactionFailedAsync(
+ forwarderWrapper.marketSellEthForERC20Async(signedOrdersWithFee, feeOrders, {
+ value: fillAmount,
+ from: takerAddress,
+ }),
+ RevertReason.TransferFailed,
+ );
+ });
+ it('throws when mixed ERC721 and ERC20 assets with ERC20 first', async () => {
+ const makerAssetId = erc721MakerAssetIds[0];
+ const erc721SignedOrder = orderFactory.newSignedOrder({
+ makerAssetAmount: new BigNumber(1),
+ makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
+ });
+ const erc20SignedOrder = orderFactory.newSignedOrder();
+ signedOrders = [erc20SignedOrder, erc721SignedOrder];
+ const fillAmountWei = erc20SignedOrder.takerAssetAmount.plus(erc721SignedOrder.takerAssetAmount);
+ return expectTransactionFailedAsync(
+ forwarderWrapper.marketSellEthForERC20Async(signedOrders, feeOrders, {
+ from: takerAddress,
+ value: fillAmountWei,
+ }),
+ RevertReason.InvalidOrderSignature,
+ );
+ });
+ });
+ describe('marketSellEthForERC20 with extra fees', () => {
+ it('should fill the order and send fee to fee recipient', async () => {
+ const initEthBalance = await web3Wrapper.getBalanceInWeiAsync(feeRecipientAddress);
+ const fillAmount = signedOrder.takerAssetAmount.div(2);
+ feeProportion = 150; // 1.5%
+ feeOrders = [];
+ tx = await forwarderWrapper.marketSellEthForERC20Async(
+ signedOrders,
+ feeOrders,
+ {
+ from: takerAddress,
+ value: fillAmount,
+ gasPrice: DEFAULT_GAS_PRICE,
+ },
+ {
+ feeProportion,
+ feeRecipient: feeRecipientAddress,
+ },
+ );
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ const makerBalanceBefore = erc20Balances[makerAddress][defaultMakerAssetAddress];
+ const makerBalanceAfter = newBalances[makerAddress][defaultMakerAssetAddress];
+ const takerBalanceAfter = newBalances[takerAddress][defaultMakerAssetAddress];
+ const afterEthBalance = await web3Wrapper.getBalanceInWeiAsync(feeRecipientAddress);
+ const takerBoughtAmount = takerBalanceAfter.minus(erc20Balances[takerAddress][defaultMakerAssetAddress]);
+
+ expect(makerBalanceAfter).to.be.bignumber.equal(makerBalanceBefore.minus(takerBoughtAmount));
+ expect(afterEthBalance).to.be.bignumber.equal(
+ initEthBalance.plus(fillAmount.times(feeProportion).dividedBy(10000)),
+ );
+ expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(new BigNumber(0));
+ });
+ it('should fail if the fee is set too high', async () => {
+ const initEthBalance = await web3Wrapper.getBalanceInWeiAsync(feeRecipientAddress);
+ const fillAmount = signedOrder.takerAssetAmount.div(2);
+ feeProportion = 1500; // 15.0%
+ feeOrders = [];
+ await expectTransactionFailedAsync(
+ forwarderWrapper.marketSellEthForERC20Async(
+ signedOrders,
+ feeOrders,
+ { from: takerAddress, value: fillAmount, gasPrice: DEFAULT_GAS_PRICE },
+ { feeProportion, feeRecipient: feeRecipientAddress },
+ ),
+ RevertReason.FeeProportionTooLarge,
+ );
+ const afterEthBalance = await web3Wrapper.getBalanceInWeiAsync(feeRecipientAddress);
+ expect(afterEthBalance).to.be.bignumber.equal(initEthBalance);
+ });
+ });
+ describe('marketBuyTokensWithEth', () => {
+ it('should buy the exact amount of assets', async () => {
+ const makerAssetAmount = signedOrder.makerAssetAmount.div(2);
+ const initEthBalance = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
+ const balancesBefore = await erc20Wrapper.getBalancesAsync();
+ const rate = signedOrder.makerAssetAmount.dividedBy(signedOrder.takerAssetAmount);
+ const fillAmountWei = makerAssetAmount.dividedToIntegerBy(rate);
+ feeOrders = [];
+ tx = await forwarderWrapper.marketBuyTokensWithEthAsync(signedOrders, feeOrders, makerAssetAmount, {
+ from: takerAddress,
+ value: fillAmountWei,
+ gasPrice: DEFAULT_GAS_PRICE,
+ });
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ const takerBalanceBefore = balancesBefore[takerAddress][defaultMakerAssetAddress];
+ const takerBalanceAfter = newBalances[takerAddress][defaultMakerAssetAddress];
+ const afterEthBalance = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
+ const expectedEthBalanceAfterGasCosts = initEthBalance.minus(fillAmountWei).minus(tx.gasUsed);
+ expect(takerBalanceAfter).to.be.bignumber.eq(takerBalanceBefore.plus(makerAssetAmount));
+ expect(afterEthBalance).to.be.bignumber.eq(expectedEthBalanceAfterGasCosts);
+ });
+ it('should buy the exact amount of assets and return excess ETH', async () => {
+ const makerAssetAmount = signedOrder.makerAssetAmount.div(2);
+ const initEthBalance = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
+ const balancesBefore = await erc20Wrapper.getBalancesAsync();
+ const rate = signedOrder.makerAssetAmount.dividedBy(signedOrder.takerAssetAmount);
+ const fillAmount = makerAssetAmount.dividedToIntegerBy(rate);
+ const excessFillAmount = fillAmount.times(2);
+ feeOrders = [];
+ tx = await forwarderWrapper.marketBuyTokensWithEthAsync(signedOrders, feeOrders, makerAssetAmount, {
+ from: takerAddress,
+ value: excessFillAmount,
+ gasPrice: DEFAULT_GAS_PRICE,
+ });
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ const takerBalanceBefore = balancesBefore[takerAddress][defaultMakerAssetAddress];
+ const takerBalanceAfter = newBalances[takerAddress][defaultMakerAssetAddress];
+ const afterEthBalance = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
+ const expectedEthBalanceAfterGasCosts = initEthBalance.minus(fillAmount).minus(tx.gasUsed);
+ expect(takerBalanceAfter).to.be.bignumber.eq(takerBalanceBefore.plus(makerAssetAmount));
+ expect(afterEthBalance).to.be.bignumber.eq(expectedEthBalanceAfterGasCosts);
+ });
+ it('should buy the exact amount of assets with fee abstraction', async () => {
+ const makerAssetAmount = signedOrder.makerAssetAmount.div(2);
+ const balancesBefore = await erc20Wrapper.getBalancesAsync();
+ const rate = signedOrder.makerAssetAmount.dividedBy(signedOrder.takerAssetAmount);
+ const fillAmount = makerAssetAmount.dividedToIntegerBy(rate);
+ const excessFillAmount = fillAmount.times(2);
+ tx = await forwarderWrapper.marketBuyTokensWithEthAsync(signedOrdersWithFee, feeOrders, makerAssetAmount, {
+ from: takerAddress,
+ value: excessFillAmount,
+ });
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ const takerBalanceBefore = balancesBefore[takerAddress][defaultMakerAssetAddress];
+ const takerBalanceAfter = newBalances[takerAddress][defaultMakerAssetAddress];
+ expect(takerBalanceAfter).to.be.bignumber.eq(takerBalanceBefore.plus(makerAssetAmount));
+ });
+ it('should buy the exact amount of assets when buying zrx with fee abstraction', async () => {
+ signedOrder = orderFactory.newSignedOrder({
+ makerAssetData: assetProxyUtils.encodeERC20AssetData(zrxToken.address),
+ takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT),
+ });
+ signedOrdersWithFee = [signedOrder];
+ feeOrders = [];
+ const makerAssetAmount = signedOrder.makerAssetAmount.div(2);
+ const takerWeiBalanceBefore = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
+ const balancesBefore = await erc20Wrapper.getBalancesAsync();
+ const fillAmountWei = await forwarderWrapper.calculateMarketBuyFillAmountWeiAsync(
+ signedOrdersWithFee,
+ feeOrders,
+ feeProportion,
+ makerAssetAmount,
+ );
+ tx = await forwarderWrapper.marketBuyTokensWithEthAsync(signedOrdersWithFee, feeOrders, makerAssetAmount, {
+ from: takerAddress,
+ value: fillAmountWei,
+ gasPrice: DEFAULT_GAS_PRICE,
+ });
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ const takerTokenBalanceBefore = balancesBefore[takerAddress][zrxToken.address];
+ const takerTokenBalanceAfter = newBalances[takerAddress][zrxToken.address];
+ const takerWeiBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
+ const expectedCostAfterGas = fillAmountWei.plus(tx.gasUsed);
+ expect(takerTokenBalanceAfter).to.be.bignumber.greaterThan(takerTokenBalanceBefore.plus(makerAssetAmount));
+ expect(takerWeiBalanceAfter).to.be.bignumber.equal(takerWeiBalanceBefore.minus(expectedCostAfterGas));
+ });
+ it('throws if fees are higher than 5% when buying zrx', async () => {
+ const highFeeZRXOrder = orderFactory.newSignedOrder({
+ makerAssetData: assetProxyUtils.encodeERC20AssetData(zrxToken.address),
+ makerAssetAmount: signedOrder.makerAssetAmount,
+ takerFee: signedOrder.makerAssetAmount.times(0.06),
+ });
+ signedOrdersWithFee = [highFeeZRXOrder];
+ feeOrders = [];
+ const makerAssetAmount = signedOrder.makerAssetAmount.div(2);
+ const fillAmountWei = await forwarderWrapper.calculateMarketBuyFillAmountWeiAsync(
+ signedOrdersWithFee,
+ feeOrders,
+ feeProportion,
+ makerAssetAmount,
+ );
+ return expectTransactionFailedAsync(
+ forwarderWrapper.marketBuyTokensWithEthAsync(signedOrdersWithFee, feeOrders, makerAssetAmount, {
+ from: takerAddress,
+ value: fillAmountWei,
+ }),
+ RevertReason.UnacceptableThreshold,
+ );
+ });
+ it('throws if fees are higher than 5% when buying erc20', async () => {
+ const highFeeERC20Order = orderFactory.newSignedOrder({
+ takerFee: signedOrder.makerAssetAmount.times(0.06),
+ });
+ signedOrdersWithFee = [highFeeERC20Order];
+ feeOrders = [feeOrder];
+ const makerAssetAmount = signedOrder.makerAssetAmount.div(2);
+ const fillAmountWei = await forwarderWrapper.calculateMarketBuyFillAmountWeiAsync(
+ signedOrdersWithFee,
+ feeOrders,
+ feeProportion,
+ makerAssetAmount,
+ );
+ return expectTransactionFailedAsync(
+ forwarderWrapper.marketBuyTokensWithEthAsync(signedOrdersWithFee, feeOrders, makerAssetAmount, {
+ from: takerAddress,
+ value: fillAmountWei,
+ }),
+ RevertReason.UnacceptableThreshold as any,
+ );
+ });
+ it('throws if makerAssetAmount is 0', async () => {
+ const makerAssetAmount = new BigNumber(0);
+ const fillAmountWei = await forwarderWrapper.calculateMarketBuyFillAmountWeiAsync(
+ signedOrdersWithFee,
+ feeOrders,
+ feeProportion,
+ makerAssetAmount,
+ );
+ return expectTransactionFailedAsync(
+ forwarderWrapper.marketBuyTokensWithEthAsync(signedOrdersWithFee, feeOrders, makerAssetAmount, {
+ from: takerAddress,
+ value: fillAmountWei,
+ }),
+ RevertReason.ValueGreaterThanZero as any,
+ );
+ });
+ it('throws if the amount of ETH sent in is less than the takerAssetFilledAmount', async () => {
+ const makerAssetAmount = signedOrder.makerAssetAmount;
+ const fillAmount = signedOrder.takerAssetAmount.div(2);
+ const zero = new BigNumber(0);
+ // Deposit enough taker balance to fill the order
+ const wethDepositTxHash = await wethContract.deposit.sendTransactionAsync({
+ from: takerAddress,
+ value: signedOrder.takerAssetAmount,
+ });
+ await web3Wrapper.awaitTransactionSuccessAsync(wethDepositTxHash);
+ // Transfer all of this WETH to the forwarding contract
+ const wethTransferTxHash = await wethContract.transfer.sendTransactionAsync(
+ forwarderContract.address,
+ signedOrder.takerAssetAmount,
+ { from: takerAddress },
+ );
+ await web3Wrapper.awaitTransactionSuccessAsync(wethTransferTxHash);
+ // We use the contract directly to get around wrapper validations and calculations
+ const formattedOrders = formatters.createMarketSellOrders(signedOrders, zero);
+ const formattedFeeOrders = formatters.createMarketSellOrders(feeOrders, zero);
+ return expectTransactionFailedAsync(
+ forwarderContract.marketBuyTokensWithEth.sendTransactionAsync(
+ formattedOrders.orders,
+ formattedOrders.signatures,
+ formattedFeeOrders.orders,
+ formattedFeeOrders.signatures,
+ makerAssetAmount,
+ zero,
+ constants.NULL_ADDRESS,
+ { value: fillAmount, from: takerAddress },
+ ),
+ RevertReason.InvalidMsgValue,
+ );
+ });
+ });
+ describe('marketBuyTokensWithEth - ERC721', async () => {
+ it('buys ERC721 assets', async () => {
+ const makerAssetId = erc721MakerAssetIds[0];
+ signedOrder = orderFactory.newSignedOrder({
+ makerAssetAmount: new BigNumber(1),
+ makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
+ });
+ feeOrders = [];
+ signedOrders = [signedOrder];
+ const makerAssetAmount = new BigNumber(signedOrders.length);
+ const fillAmountWei = await forwarderWrapper.calculateMarketBuyFillAmountWeiAsync(
+ signedOrders,
+ feeOrders,
+ feeProportion,
+ makerAssetAmount,
+ );
+ tx = await forwarderWrapper.marketBuyTokensWithEthAsync(signedOrders, feeOrders, makerAssetAmount, {
+ from: takerAddress,
+ value: fillAmountWei,
+ });
+ const newOwnerTakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId);
+ expect(newOwnerTakerAsset).to.be.bignumber.equal(takerAddress);
+ });
+ it('buys ERC721 assets with fee abstraction', async () => {
+ const makerAssetId = erc721MakerAssetIds[0];
+ signedOrder = orderFactory.newSignedOrder({
+ makerAssetAmount: new BigNumber(1),
+ takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT),
+ makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
+ });
+ signedOrders = [signedOrder];
+ const makerAssetAmount = new BigNumber(signedOrders.length);
+ const fillAmountWei = await forwarderWrapper.calculateMarketBuyFillAmountWeiAsync(
+ signedOrders,
+ feeOrders,
+ feeProportion,
+ makerAssetAmount,
+ );
+ tx = await forwarderWrapper.marketBuyTokensWithEthAsync(signedOrders, feeOrders, makerAssetAmount, {
+ from: takerAddress,
+ value: fillAmountWei,
+ });
+ const newOwnerTakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId);
+ expect(newOwnerTakerAsset).to.be.bignumber.equal(takerAddress);
+ });
+ it('buys ERC721 assets with fee abstraction and pays fee to fee recipient', async () => {
+ const makerAssetId = erc721MakerAssetIds[0];
+ signedOrder = orderFactory.newSignedOrder({
+ makerAssetAmount: new BigNumber(1),
+ takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT),
+ makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
+ });
+ signedOrders = [signedOrder];
+ feeProportion = 100;
+ const initTakerBalanceWei = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
+ const initFeeRecipientBalanceWei = await web3Wrapper.getBalanceInWeiAsync(feeRecipientAddress);
+ const makerAssetAmount = new BigNumber(signedOrders.length);
+ const fillAmountWei = await forwarderWrapper.calculateMarketBuyFillAmountWeiAsync(
+ signedOrders,
+ feeOrders,
+ feeProportion,
+ makerAssetAmount,
+ );
+ tx = await forwarderWrapper.marketBuyTokensWithEthAsync(
+ signedOrders,
+ feeOrders,
+ makerAssetAmount,
+ {
+ from: takerAddress,
+ value: fillAmountWei,
+ gasPrice: DEFAULT_GAS_PRICE,
+ },
+ {
+ feeProportion,
+ feeRecipient: feeRecipientAddress,
+ },
+ );
+ const afterFeeRecipientEthBalance = await web3Wrapper.getBalanceInWeiAsync(feeRecipientAddress);
+ const afterTakerBalanceWei = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
+ const takerFilledAmount = initTakerBalanceWei.minus(afterTakerBalanceWei).plus(tx.gasUsed);
+ const newOwnerTakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId);
+ expect(newOwnerTakerAsset).to.be.bignumber.equal(takerAddress);
+ const balanceDiff = afterFeeRecipientEthBalance.minus(initFeeRecipientBalanceWei);
+ expect(takerFilledAmount.dividedToIntegerBy(balanceDiff)).to.be.bignumber.equal(101);
+ expect(takerFilledAmount.minus(balanceDiff).dividedToIntegerBy(balanceDiff)).to.be.bignumber.equal(100);
+ });
+ it('buys multiple ERC721 assets with fee abstraction and pays fee to fee recipient', async () => {
+ const makerAssetId1 = erc721MakerAssetIds[0];
+ const makerAssetId2 = erc721MakerAssetIds[1];
+ const signedOrder1 = orderFactory.newSignedOrder({
+ makerAssetAmount: new BigNumber(1),
+ takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), DECIMALS_DEFAULT),
+ makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId1),
+ });
+ const signedOrder2 = orderFactory.newSignedOrder({
+ makerAssetAmount: new BigNumber(1),
+ takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), DECIMALS_DEFAULT),
+ makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId2),
+ });
+ signedOrders = [signedOrder1, signedOrder2];
+ feeProportion = 10;
+ const makerAssetAmount = new BigNumber(signedOrders.length);
+ const fillAmountWei = await forwarderWrapper.calculateMarketBuyFillAmountWeiAsync(
+ signedOrders,
+ feeOrders,
+ feeProportion,
+ makerAssetAmount,
+ );
+ tx = await forwarderWrapper.marketBuyTokensWithEthAsync(signedOrders, feeOrders, makerAssetAmount, {
+ from: takerAddress,
+ value: fillAmountWei,
+ });
+ const newOwnerTakerAsset1 = await erc721Token.ownerOf.callAsync(makerAssetId1);
+ expect(newOwnerTakerAsset1).to.be.bignumber.equal(takerAddress);
+ const newOwnerTakerAsset2 = await erc721Token.ownerOf.callAsync(makerAssetId2);
+ expect(newOwnerTakerAsset2).to.be.bignumber.equal(takerAddress);
+ });
+ it('buys ERC721 assets with fee abstraction and handles fee orders filled and excess eth', async () => {
+ const makerAssetId = erc721MakerAssetIds[0];
+ feeProportion = 0;
+ // In this scenario a total of 6 ZRX fees need to be paid.
+ // There are two fee orders, but the first fee order is partially filled while
+ // the Forwarding contract tx is in the mempool.
+ const erc721MakerAssetAmount = new BigNumber(1);
+ signedOrder = orderFactory.newSignedOrder({
+ makerAssetAmount: erc721MakerAssetAmount,
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), DECIMALS_DEFAULT),
+ takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(6), DECIMALS_DEFAULT),
+ makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
+ });
+ signedOrders = [signedOrder];
+ const firstFeeOrder = orderFactory.newSignedOrder({
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(8), DECIMALS_DEFAULT),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(0.1), DECIMALS_DEFAULT),
+ makerAssetData: assetProxyUtils.encodeERC20AssetData(zrxToken.address),
+ takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(0), DECIMALS_DEFAULT),
+ });
+ const secondFeeOrder = orderFactory.newSignedOrder({
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(8), DECIMALS_DEFAULT),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(0.12), DECIMALS_DEFAULT),
+ makerAssetData: assetProxyUtils.encodeERC20AssetData(zrxToken.address),
+ takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(0), DECIMALS_DEFAULT),
+ });
+ feeOrders = [firstFeeOrder, secondFeeOrder];
+ const makerAssetAmount = new BigNumber(signedOrders.length);
+ const fillAmountWei = await forwarderWrapper.calculateMarketBuyFillAmountWeiAsync(
+ signedOrders,
+ feeOrders,
+ feeProportion,
+ erc721MakerAssetAmount,
+ );
+ // Simulate another otherAddress user partially filling firstFeeOrder
+ const firstFeeOrderFillAmount = firstFeeOrder.makerAssetAmount.div(2);
+ tx = await forwarderWrapper.marketBuyTokensWithEthAsync([firstFeeOrder], [], firstFeeOrderFillAmount, {
+ from: otherAddress,
+ value: fillAmountWei,
+ });
+ // For tests we calculate how much this should've cost given that firstFeeOrder was filled
+ const expectedFillAmountWei = await forwarderWrapper.calculateMarketBuyFillAmountWeiAsync(
+ signedOrders,
+ feeOrders,
+ feeProportion,
+ erc721MakerAssetAmount,
+ );
+ // With 4 ZRX remaining in firstFeeOrder, the secondFeeOrder will need to be filled to make up
+ // the total amount of fees required (6)
+ // Since the fee orders can be filled while the transaction is pending the user safely sends in
+ // extra ether to cover any slippage
+ const initEthBalance = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
+ const slippageFillAmountWei = fillAmountWei.times(2);
+ tx = await forwarderWrapper.marketBuyTokensWithEthAsync(signedOrders, feeOrders, makerAssetAmount, {
+ from: takerAddress,
+ value: slippageFillAmountWei,
+ gasPrice: DEFAULT_GAS_PRICE,
+ });
+ const afterEthBalance = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
+ const expectedEthBalanceAfterGasCosts = initEthBalance.minus(expectedFillAmountWei).minus(tx.gasUsed);
+ const newOwnerTakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId);
+ expect(newOwnerTakerAsset).to.be.bignumber.equal(takerAddress);
+ expect(afterEthBalance).to.be.bignumber.equal(expectedEthBalanceAfterGasCosts);
+ });
+ it('buys ERC721 assets with fee abstraction and handles fee orders filled', async () => {
+ const makerAssetId = erc721MakerAssetIds[0];
+ feeProportion = 0;
+ // In this scenario a total of 6 ZRX fees need to be paid.
+ // There are two fee orders, but the first fee order is partially filled while
+ // the Forwarding contract tx is in the mempool.
+ const erc721MakerAssetAmount = new BigNumber(1);
+ signedOrder = orderFactory.newSignedOrder({
+ makerAssetAmount: erc721MakerAssetAmount,
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), DECIMALS_DEFAULT),
+ takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(6), DECIMALS_DEFAULT),
+ makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
+ });
+ const zrxMakerAssetAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(8), DECIMALS_DEFAULT);
+ signedOrders = [signedOrder];
+ const firstFeeOrder = orderFactory.newSignedOrder({
+ makerAssetAmount: zrxMakerAssetAmount,
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(0.1), DECIMALS_DEFAULT),
+ makerAssetData: assetProxyUtils.encodeERC20AssetData(zrxToken.address),
+ takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(0), DECIMALS_DEFAULT),
+ });
+ const secondFeeOrder = orderFactory.newSignedOrder({
+ makerAssetAmount: zrxMakerAssetAmount,
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(0.12), DECIMALS_DEFAULT),
+ makerAssetData: assetProxyUtils.encodeERC20AssetData(zrxToken.address),
+ takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(0), DECIMALS_DEFAULT),
+ });
+ feeOrders = [firstFeeOrder, secondFeeOrder];
+ const makerAssetAmount = new BigNumber(signedOrders.length);
+ const fillAmountWei = await forwarderWrapper.calculateMarketBuyFillAmountWeiAsync(
+ signedOrders,
+ feeOrders,
+ feeProportion,
+ erc721MakerAssetAmount,
+ );
+ // Simulate another otherAddress user partially filling firstFeeOrder
+ const firstFeeOrderFillAmount = firstFeeOrder.makerAssetAmount.div(2);
+ tx = await forwarderWrapper.marketBuyTokensWithEthAsync([firstFeeOrder], [], firstFeeOrderFillAmount, {
+ from: otherAddress,
+ value: fillAmountWei,
+ });
+ const expectedFillAmountWei = await forwarderWrapper.calculateMarketBuyFillAmountWeiAsync(
+ signedOrders,
+ feeOrders,
+ feeProportion,
+ erc721MakerAssetAmount,
+ );
+ tx = await forwarderWrapper.marketBuyTokensWithEthAsync(signedOrders, feeOrders, makerAssetAmount, {
+ from: takerAddress,
+ value: expectedFillAmountWei,
+ });
+ const newOwnerTakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId);
+ expect(newOwnerTakerAsset).to.be.bignumber.equal(takerAddress);
+ });
+ it('throws when mixed ERC721 and ERC20 assets', async () => {
+ const makerAssetId = erc721MakerAssetIds[0];
+ const erc721SignedOrder = orderFactory.newSignedOrder({
+ makerAssetAmount: new BigNumber(1),
+ makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
+ });
+ const erc20SignedOrder = orderFactory.newSignedOrder();
+ signedOrders = [erc721SignedOrder, erc20SignedOrder];
+ const makerAssetAmount = new BigNumber(signedOrders.length);
+ const fillAmountWei = erc20SignedOrder.takerAssetAmount.plus(erc721SignedOrder.takerAssetAmount);
+ return expectTransactionFailedAsync(
+ forwarderWrapper.marketBuyTokensWithEthAsync(signedOrders, feeOrders, makerAssetAmount, {
+ from: takerAddress,
+ value: fillAmountWei,
+ }),
+ RevertReason.LibBytesGreaterOrEqualTo32LengthRequired,
+ );
+ });
+ it('throws when mixed ERC721 and ERC20 assets with ERC20 first', async () => {
+ const makerAssetId = erc721MakerAssetIds[0];
+ const erc721SignedOrder = orderFactory.newSignedOrder({
+ makerAssetAmount: new BigNumber(1),
+ makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
+ });
+ const erc20SignedOrder = orderFactory.newSignedOrder();
+ signedOrders = [erc20SignedOrder, erc721SignedOrder];
+ const makerAssetAmount = new BigNumber(signedOrders.length);
+ const fillAmountWei = erc20SignedOrder.takerAssetAmount.plus(erc721SignedOrder.takerAssetAmount);
+ return expectTransactionFailedAsync(
+ forwarderWrapper.marketBuyTokensWithEthAsync(signedOrders, feeOrders, makerAssetAmount, {
+ from: takerAddress,
+ value: fillAmountWei,
+ }),
+ RevertReason.InvalidTakerAmount,
+ );
+ });
+ });
+});
+// tslint:disable:max-file-line-count
+// tslint:enable:no-unnecessary-type-assertion
diff --git a/packages/contracts/test/multisig/asset_proxy_owner.ts b/packages/contracts/test/multisig/asset_proxy_owner.ts
index 16231dfcb..10fed6815 100644
--- a/packages/contracts/test/multisig/asset_proxy_owner.ts
+++ b/packages/contracts/test/multisig/asset_proxy_owner.ts
@@ -4,11 +4,11 @@ import * as chai from 'chai';
import { LogWithDecodedArgs } from 'ethereum-types';
import {
+ AssetProxyOwnerAssetProxyRegistrationEventArgs,
AssetProxyOwnerContract,
- AssetProxyRegistrationContractEventArgs,
- ExecutionContractEventArgs,
- ExecutionFailureContractEventArgs,
- SubmissionContractEventArgs,
+ AssetProxyOwnerExecutionEventArgs,
+ AssetProxyOwnerExecutionFailureEventArgs,
+ AssetProxyOwnerSubmissionEventArgs,
} from '../../generated_contract_wrappers/asset_proxy_owner';
import { MixinAuthorizableContract } from '../../generated_contract_wrappers/mixin_authorizable';
import { TestAssetProxyOwnerContract } from '../../generated_contract_wrappers/test_asset_proxy_owner';
@@ -171,14 +171,16 @@ describe('AssetProxyOwner', () => {
owners[0],
);
- const log = submitTxRes.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
+ const log = submitTxRes.logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>;
const txId = log.args.transactionId;
await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
await increaseTimeAndMineBlockAsync(SECONDS_TIME_LOCKED.toNumber());
const executeTxRes = await multiSigWrapper.executeTransactionAsync(txId, owners[0]);
- const registerLog = executeTxRes.logs[0] as LogWithDecodedArgs<AssetProxyRegistrationContractEventArgs>;
+ const registerLog = executeTxRes.logs[0] as LogWithDecodedArgs<
+ AssetProxyOwnerAssetProxyRegistrationEventArgs
+ >;
expect(registerLog.args.assetProxyContract).to.equal(addressToRegister);
expect(registerLog.args.isRegistered).to.equal(isRegistered);
@@ -200,14 +202,14 @@ describe('AssetProxyOwner', () => {
registerAssetProxyData,
owners[0],
);
- const log = submitTxRes.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
+ const log = submitTxRes.logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>;
const txId = log.args.transactionId;
await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
await increaseTimeAndMineBlockAsync(SECONDS_TIME_LOCKED.toNumber());
const executeTxRes = await multiSigWrapper.executeTransactionAsync(txId, owners[0]);
- const failureLog = executeTxRes.logs[0] as LogWithDecodedArgs<ExecutionFailureContractEventArgs>;
+ const failureLog = executeTxRes.logs[0] as LogWithDecodedArgs<AssetProxyOwnerExecutionFailureEventArgs>;
expect(failureLog.args.transactionId).to.be.bignumber.equal(txId);
const isAssetProxyRegistered = await testAssetProxyOwner.isAssetProxyRegistered.callAsync(
@@ -234,7 +236,7 @@ describe('AssetProxyOwner', () => {
owners[0],
);
const registerAssetProxySubmitLog = registerAssetProxySubmitRes.logs[0] as LogWithDecodedArgs<
- SubmissionContractEventArgs
+ AssetProxyOwnerSubmissionEventArgs
>;
const registerAssetProxyTxId = registerAssetProxySubmitLog.args.transactionId;
await multiSigWrapper.confirmTransactionAsync(registerAssetProxyTxId, owners[1]);
@@ -251,10 +253,10 @@ describe('AssetProxyOwner', () => {
owners[0],
);
const erc20AddAuthorizedAddressSubmitLog = erc20AddAuthorizedAddressSubmitRes.logs[0] as LogWithDecodedArgs<
- SubmissionContractEventArgs
+ AssetProxyOwnerSubmissionEventArgs
>;
const erc721AddAuthorizedAddressSubmitLog = erc721AddAuthorizedAddressSubmitRes
- .logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
+ .logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>;
const erc20AddAuthorizedAddressTxId = erc20AddAuthorizedAddressSubmitLog.args.transactionId;
const erc721AddAuthorizedAddressTxId = erc721AddAuthorizedAddressSubmitLog.args.transactionId;
@@ -276,7 +278,7 @@ describe('AssetProxyOwner', () => {
notRemoveAuthorizedAddressData,
owners[0],
);
- const log = submitTxRes.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
+ const log = submitTxRes.logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>;
const txId = log.args.transactionId;
return expectContractCallFailedWithoutReasonAsync(
testAssetProxyOwner.testValidRemoveAuthorizedAddressAtIndexTx.callAsync(txId),
@@ -293,7 +295,7 @@ describe('AssetProxyOwner', () => {
removeAuthorizedAddressAtIndexData,
owners[0],
);
- const log = submitTxRes.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
+ const log = submitTxRes.logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>;
const txId = log.args.transactionId;
const isValidRemoveAuthorizedAddressAtIndexTx = await testAssetProxyOwner.testValidRemoveAuthorizedAddressAtIndexTx.callAsync(
txId,
@@ -311,7 +313,7 @@ describe('AssetProxyOwner', () => {
removeAuthorizedAddressAtIndexData,
owners[0],
);
- const log = submitTxRes.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
+ const log = submitTxRes.logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>;
const txId = log.args.transactionId;
return expectContractCallFailedWithoutReasonAsync(
testAssetProxyOwner.testValidRemoveAuthorizedAddressAtIndexTx.callAsync(txId),
@@ -330,7 +332,7 @@ describe('AssetProxyOwner', () => {
removeAuthorizedAddressAtIndexData,
owners[0],
);
- const log = res.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
+ const log = res.logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>;
const txId = log.args.transactionId;
return expectTransactionFailedWithoutReasonAsync(
@@ -350,7 +352,7 @@ describe('AssetProxyOwner', () => {
removeAuthorizedAddressAtIndexData,
owners[0],
);
- const log = res.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
+ const log = res.logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>;
const txId = log.args.transactionId;
await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
@@ -372,7 +374,7 @@ describe('AssetProxyOwner', () => {
addAuthorizedAddressData,
owners[0],
);
- const log = res.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
+ const log = res.logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>;
const txId = log.args.transactionId;
await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
@@ -394,13 +396,13 @@ describe('AssetProxyOwner', () => {
removeAuthorizedAddressAtIndexData,
owners[0],
);
- const submitLog = submitRes.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
+ const submitLog = submitRes.logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>;
const txId = submitLog.args.transactionId;
await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
const execRes = await multiSigWrapper.executeRemoveAuthorizedAddressAtIndexAsync(txId, owners[0]);
- const execLog = execRes.logs[0] as LogWithDecodedArgs<ExecutionContractEventArgs>;
+ const execLog = execRes.logs[0] as LogWithDecodedArgs<AssetProxyOwnerExecutionEventArgs>;
expect(execLog.args.transactionId).to.be.bignumber.equal(txId);
const tx = await testAssetProxyOwner.transactions.callAsync(txId);
@@ -421,13 +423,13 @@ describe('AssetProxyOwner', () => {
removeAuthorizedAddressAtIndexData,
owners[0],
);
- const submitLog = submitRes.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
+ const submitLog = submitRes.logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>;
const txId = submitLog.args.transactionId;
await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
const execRes = await multiSigWrapper.executeRemoveAuthorizedAddressAtIndexAsync(txId, owners[0]);
- const execLog = execRes.logs[0] as LogWithDecodedArgs<ExecutionContractEventArgs>;
+ const execLog = execRes.logs[0] as LogWithDecodedArgs<AssetProxyOwnerExecutionEventArgs>;
expect(execLog.args.transactionId).to.be.bignumber.equal(txId);
const tx = await testAssetProxyOwner.transactions.callAsync(txId);
diff --git a/packages/contracts/test/multisig/multi_sig_with_time_lock.ts b/packages/contracts/test/multisig/multi_sig_with_time_lock.ts
index a7b99d867..a8e9243a9 100644
--- a/packages/contracts/test/multisig/multi_sig_with_time_lock.ts
+++ b/packages/contracts/test/multisig/multi_sig_with_time_lock.ts
@@ -5,7 +5,7 @@ import { LogWithDecodedArgs } from 'ethereum-types';
import {
MultiSigWalletWithTimeLockContract,
- SubmissionContractEventArgs,
+ MultiSigWalletWithTimeLockSubmissionEventArgs,
} from '../../generated_contract_wrappers/multi_sig_wallet_with_time_lock';
import { artifacts } from '../utils/artifacts';
import { expectTransactionFailedWithoutReasonAsync } from '../utils/assertions';
@@ -76,7 +76,7 @@ describe('MultiSigWalletWithTimeLock', () => {
const destination = multiSig.address;
const changeTimeLockData = multiSig.changeTimeLock.getABIEncodedTransactionData(SECONDS_TIME_LOCKED);
const res = await multiSigWrapper.submitTransactionAsync(destination, changeTimeLockData, owners[0]);
- const log = res.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
+ const log = res.logs[0] as LogWithDecodedArgs<MultiSigWalletWithTimeLockSubmissionEventArgs>;
const txId = log.args.transactionId;
return expectTransactionFailedWithoutReasonAsync(
multiSig.executeTransaction.sendTransactionAsync(txId, { from: owners[0] }),
@@ -87,7 +87,7 @@ describe('MultiSigWalletWithTimeLock', () => {
const destination = multiSig.address;
const changeTimeLockData = multiSig.changeTimeLock.getABIEncodedTransactionData(SECONDS_TIME_LOCKED);
const subRes = await multiSigWrapper.submitTransactionAsync(destination, changeTimeLockData, owners[0]);
- const subLog = subRes.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
+ const subLog = subRes.logs[0] as LogWithDecodedArgs<MultiSigWalletWithTimeLockSubmissionEventArgs>;
const txId = subLog.args.transactionId;
const confirmRes = await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
@@ -105,7 +105,7 @@ describe('MultiSigWalletWithTimeLock', () => {
const destination = multiSig.address;
const changeTimeLockData = multiSig.changeTimeLock.getABIEncodedTransactionData(SECONDS_TIME_LOCKED);
const subRes = await multiSigWrapper.submitTransactionAsync(destination, changeTimeLockData, owners[0]);
- const subLog = subRes.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
+ const subLog = subRes.logs[0] as LogWithDecodedArgs<MultiSigWalletWithTimeLockSubmissionEventArgs>;
const txId = subLog.args.transactionId;
await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
@@ -141,7 +141,7 @@ describe('MultiSigWalletWithTimeLock', () => {
changeTimeLockData,
owners[0],
);
- const log = res.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
+ const log = res.logs[0] as LogWithDecodedArgs<MultiSigWalletWithTimeLockSubmissionEventArgs>;
txId = log.args.transactionId;
await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
});
diff --git a/packages/contracts/test/tokens/unlimited_allowance_token.ts b/packages/contracts/test/tokens/unlimited_allowance_token.ts
index 8a3b20d0f..81d931fc5 100644
--- a/packages/contracts/test/tokens/unlimited_allowance_token.ts
+++ b/packages/contracts/test/tokens/unlimited_allowance_token.ts
@@ -3,7 +3,7 @@ import { RevertReason } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import * as chai from 'chai';
-import { DummyERC20TokenContract } from '../../generated_contract_wrappers/dummy_e_r_c20_token';
+import { DummyERC20TokenContract } from '../../generated_contract_wrappers/dummy_erc20_token';
import { artifacts } from '../utils/artifacts';
import { expectContractCallFailed } from '../utils/assertions';
import { chaiSetup } from '../utils/chai_setup';
diff --git a/packages/contracts/test/utils/artifacts.ts b/packages/contracts/test/utils/artifacts.ts
index 23e93c085..d3f808218 100644
--- a/packages/contracts/test/utils/artifacts.ts
+++ b/packages/contracts/test/utils/artifacts.ts
@@ -8,6 +8,7 @@ import * as ERC20Proxy from '../../artifacts/ERC20Proxy.json';
import * as ERC721Proxy from '../../artifacts/ERC721Proxy.json';
import * as Exchange from '../../artifacts/Exchange.json';
import * as ExchangeWrapper from '../../artifacts/ExchangeWrapper.json';
+import * as Forwarder from '../../artifacts/Forwarder.json';
import * as IAssetProxy from '../../artifacts/IAssetProxy.json';
import * as MixinAuthorizable from '../../artifacts/MixinAuthorizable.json';
import * as MultiSigWallet from '../../artifacts/MultiSigWallet.json';
@@ -34,6 +35,7 @@ export const artifacts = {
Exchange: (Exchange as any) as ContractArtifact,
ExchangeWrapper: (ExchangeWrapper as any) as ContractArtifact,
EtherToken: (EtherToken as any) as ContractArtifact,
+ Forwarder: (Forwarder as any) as ContractArtifact,
IAssetProxy: (IAssetProxy as any) as ContractArtifact,
MixinAuthorizable: (MixinAuthorizable as any) as ContractArtifact,
MultiSigWallet: (MultiSigWallet as any) as ContractArtifact,
diff --git a/packages/contracts/test/utils/core_combinatorial_utils.ts b/packages/contracts/test/utils/core_combinatorial_utils.ts
index 8c6c83014..7c16ef201 100644
--- a/packages/contracts/test/utils/core_combinatorial_utils.ts
+++ b/packages/contracts/test/utils/core_combinatorial_utils.ts
@@ -14,7 +14,7 @@ import { LogWithDecodedArgs, Provider, TxData } from 'ethereum-types';
import * as _ from 'lodash';
import 'make-promises-safe';
-import { ExchangeContract, FillContractEventArgs } from '../../generated_contract_wrappers/exchange';
+import { ExchangeContract, ExchangeFillEventArgs } from '../../generated_contract_wrappers/exchange';
import { artifacts } from './artifacts';
import { expectTransactionFailedAsync } from './assertions';
@@ -467,7 +467,7 @@ export class CoreCombinatorialUtils {
expect(txReceipt.logs.length).to.be.equal(1, 'logs length');
// tslint:disable-next-line:no-unnecessary-type-assertion
- const log = txReceipt.logs[0] as LogWithDecodedArgs<FillContractEventArgs>;
+ const log = txReceipt.logs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>;
expect(log.args.makerAddress).to.be.equal(makerAddress, 'log.args.makerAddress');
expect(log.args.takerAddress).to.be.equal(this.takerAddress, 'log.args.this.takerAddress');
expect(log.args.feeRecipientAddress).to.be.equal(feeRecipient, 'log.args.feeRecipientAddress');
diff --git a/packages/contracts/test/utils/erc20_wrapper.ts b/packages/contracts/test/utils/erc20_wrapper.ts
index 53e9791bc..cf1433791 100644
--- a/packages/contracts/test/utils/erc20_wrapper.ts
+++ b/packages/contracts/test/utils/erc20_wrapper.ts
@@ -4,8 +4,8 @@ import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { Provider } from 'ethereum-types';
import * as _ from 'lodash';
-import { DummyERC20TokenContract } from '../../generated_contract_wrappers/dummy_e_r_c20_token';
-import { ERC20ProxyContract } from '../../generated_contract_wrappers/e_r_c20_proxy';
+import { DummyERC20TokenContract } from '../../generated_contract_wrappers/dummy_erc20_token';
+import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_proxy';
import { artifacts } from './artifacts';
import { constants } from './constants';
@@ -138,6 +138,14 @@ export class ERC20Wrapper {
});
return balancesByOwner;
}
+ public addDummyTokenContract(dummy: DummyERC20TokenContract): void {
+ if (!_.isUndefined(this._dummyTokenContracts)) {
+ this._dummyTokenContracts.push(dummy);
+ }
+ }
+ public addTokenOwnerAddress(address: string): void {
+ this._tokenOwnerAddresses.push(address);
+ }
public getTokenOwnerAddresses(): string[] {
return this._tokenOwnerAddresses;
}
diff --git a/packages/contracts/test/utils/erc721_wrapper.ts b/packages/contracts/test/utils/erc721_wrapper.ts
index 6347f56e7..a38dfb811 100644
--- a/packages/contracts/test/utils/erc721_wrapper.ts
+++ b/packages/contracts/test/utils/erc721_wrapper.ts
@@ -4,8 +4,8 @@ import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { Provider } from 'ethereum-types';
import * as _ from 'lodash';
-import { DummyERC721TokenContract } from '../../generated_contract_wrappers/dummy_e_r_c721_token';
-import { ERC721ProxyContract } from '../../generated_contract_wrappers/e_r_c721_proxy';
+import { DummyERC721TokenContract } from '../../generated_contract_wrappers/dummy_erc721_token';
+import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy';
import { artifacts } from './artifacts';
import { constants } from './constants';
diff --git a/packages/contracts/test/utils/forwarder_wrapper.ts b/packages/contracts/test/utils/forwarder_wrapper.ts
new file mode 100644
index 000000000..d227420ee
--- /dev/null
+++ b/packages/contracts/test/utils/forwarder_wrapper.ts
@@ -0,0 +1,220 @@
+import { assetProxyUtils } from '@0xproject/order-utils';
+import { AssetProxyId, SignedOrder } from '@0xproject/types';
+import { BigNumber } from '@0xproject/utils';
+import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import { Provider, TransactionReceiptWithDecodedLogs, TxDataPayable } from 'ethereum-types';
+import * as _ from 'lodash';
+
+import { ForwarderContract } from '../../generated_contract_wrappers/forwarder';
+
+import { constants } from './constants';
+import { formatters } from './formatters';
+import { LogDecoder } from './log_decoder';
+import { MarketSellOrders } from './types';
+
+const DEFAULT_FEE_PROPORTION = 0;
+const PERCENTAGE_DENOMINATOR = 10000;
+const ZERO_AMOUNT = new BigNumber(0);
+const INSUFFICENT_ORDERS_FOR_MAKER_AMOUNT = 'Unable to satisfy makerAssetFillAmount with provided orders';
+
+export class ForwarderWrapper {
+ private _web3Wrapper: Web3Wrapper;
+ private _forwarderContract: ForwarderContract;
+ private _logDecoder: LogDecoder;
+ private _zrxAddress: string;
+ private static _createOptimizedSellOrders(signedOrders: SignedOrder[]): MarketSellOrders {
+ const marketSellOrders = formatters.createMarketSellOrders(signedOrders, ZERO_AMOUNT);
+ const assetDataId = assetProxyUtils.decodeAssetDataId(signedOrders[0].makerAssetData);
+ // Contract will fill this in for us as all of the assetData is assumed to be the same
+ for (let i = 0; i < signedOrders.length; i++) {
+ if (i !== 0 && assetDataId === AssetProxyId.ERC20) {
+ // Forwarding contract will fill this in from the first order
+ marketSellOrders.orders[i].makerAssetData = constants.NULL_BYTES;
+ }
+ marketSellOrders.orders[i].takerAssetData = constants.NULL_BYTES;
+ }
+ return marketSellOrders;
+ }
+ private static _createOptimizedZRXSellOrders(signedOrders: SignedOrder[]): MarketSellOrders {
+ const marketSellOrders = formatters.createMarketSellOrders(signedOrders, ZERO_AMOUNT);
+ // Contract will fill this in for us as all of the assetData is assumed to be the same
+ for (let i = 0; i < signedOrders.length; i++) {
+ marketSellOrders.orders[i].makerAssetData = constants.NULL_BYTES;
+ marketSellOrders.orders[i].takerAssetData = constants.NULL_BYTES;
+ }
+ return marketSellOrders;
+ }
+ private static _calculateAdditionalFeeProportionAmount(feeProportion: number, fillAmountWei: BigNumber): BigNumber {
+ if (feeProportion > 0) {
+ // Add to the total ETH transaction to ensure all NFTs can be filled after fees
+ // 150 = 1.5% = 0.015
+ const denominator = new BigNumber(1).minus(new BigNumber(feeProportion).dividedBy(PERCENTAGE_DENOMINATOR));
+ return fillAmountWei.dividedBy(denominator).round(0, BigNumber.ROUND_FLOOR);
+ }
+ return fillAmountWei;
+ }
+ constructor(contractInstance: ForwarderContract, provider: Provider, zrxAddress: string) {
+ this._forwarderContract = contractInstance;
+ this._web3Wrapper = new Web3Wrapper(provider);
+ this._logDecoder = new LogDecoder(this._web3Wrapper, this._forwarderContract.address);
+ // this._web3Wrapper.abiDecoder.addABI(contractInstance.abi);
+ this._zrxAddress = zrxAddress;
+ }
+ public async marketBuyTokensWithEthAsync(
+ orders: SignedOrder[],
+ feeOrders: SignedOrder[],
+ makerTokenBuyAmount: BigNumber,
+ txData: TxDataPayable,
+ opts: { feeProportion?: number; feeRecipient?: string } = {},
+ ): Promise<TransactionReceiptWithDecodedLogs> {
+ const params = ForwarderWrapper._createOptimizedSellOrders(orders);
+ const feeParams = ForwarderWrapper._createOptimizedZRXSellOrders(feeOrders);
+ const feeProportion = _.isUndefined(opts.feeProportion) ? DEFAULT_FEE_PROPORTION : opts.feeProportion;
+ const feeRecipient = _.isUndefined(opts.feeRecipient) ? constants.NULL_ADDRESS : opts.feeRecipient;
+ const txHash: string = await this._forwarderContract.marketBuyTokensWithEth.sendTransactionAsync(
+ params.orders,
+ params.signatures,
+ feeParams.orders,
+ feeParams.signatures,
+ makerTokenBuyAmount,
+ feeProportion,
+ feeRecipient,
+ txData,
+ );
+ const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
+ return tx;
+ }
+ public async marketSellEthForERC20Async(
+ orders: SignedOrder[],
+ feeOrders: SignedOrder[],
+ txData: TxDataPayable,
+ opts: { feeProportion?: number; feeRecipient?: string } = {},
+ ): Promise<TransactionReceiptWithDecodedLogs> {
+ const assetDataId = assetProxyUtils.decodeAssetDataId(orders[0].makerAssetData);
+ if (assetDataId !== AssetProxyId.ERC20) {
+ throw new Error('Asset type not supported by marketSellEthForERC20');
+ }
+ const params = ForwarderWrapper._createOptimizedSellOrders(orders);
+ const feeParams = ForwarderWrapper._createOptimizedZRXSellOrders(feeOrders);
+ const feeProportion = _.isUndefined(opts.feeProportion) ? DEFAULT_FEE_PROPORTION : opts.feeProportion;
+ const feeRecipient = _.isUndefined(opts.feeRecipient) ? constants.NULL_ADDRESS : opts.feeRecipient;
+ const txHash: string = await this._forwarderContract.marketSellEthForERC20.sendTransactionAsync(
+ params.orders,
+ params.signatures,
+ feeParams.orders,
+ feeParams.signatures,
+ feeProportion,
+ feeRecipient,
+ txData,
+ );
+ const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
+ return tx;
+ }
+ public async calculateMarketBuyFillAmountWeiAsync(
+ orders: SignedOrder[],
+ feeOrders: SignedOrder[],
+ feeProportion: number,
+ makerAssetFillAmount: BigNumber,
+ ): Promise<BigNumber> {
+ const assetProxyId = assetProxyUtils.decodeAssetDataId(orders[0].makerAssetData);
+ switch (assetProxyId) {
+ case AssetProxyId.ERC20: {
+ const fillAmountWei = this._calculateMarketBuyERC20FillAmountAsync(
+ orders,
+ feeOrders,
+ feeProportion,
+ makerAssetFillAmount,
+ );
+ return fillAmountWei;
+ }
+ case AssetProxyId.ERC721: {
+ const fillAmountWei = await this._calculateMarketBuyERC721FillAmountAsync(
+ orders,
+ feeOrders,
+ feeProportion,
+ );
+ return fillAmountWei;
+ }
+ default:
+ throw new Error(`Invalid Asset Proxy Id: ${assetProxyId}`);
+ }
+ }
+ private async _calculateMarketBuyERC20FillAmountAsync(
+ orders: SignedOrder[],
+ feeOrders: SignedOrder[],
+ feeProportion: number,
+ makerAssetFillAmount: BigNumber,
+ ): Promise<BigNumber> {
+ const makerAssetData = assetProxyUtils.decodeAssetData(orders[0].makerAssetData);
+ const makerAssetToken = makerAssetData.tokenAddress;
+ const params = formatters.createMarketBuyOrders(orders, makerAssetFillAmount);
+
+ let fillAmountWei;
+ if (makerAssetToken === this._zrxAddress) {
+ // If buying ZRX we buy the tokens and fees from the ZRX order in one step
+ const expectedBuyFeeTokensFillResults = await this._forwarderContract.calculateMarketBuyZrxResults.callAsync(
+ params.orders,
+ makerAssetFillAmount,
+ );
+ if (expectedBuyFeeTokensFillResults.makerAssetFilledAmount.lessThan(makerAssetFillAmount)) {
+ throw new Error(INSUFFICENT_ORDERS_FOR_MAKER_AMOUNT);
+ }
+ fillAmountWei = expectedBuyFeeTokensFillResults.takerAssetFilledAmount;
+ } else {
+ const expectedMarketBuyFillResults = await this._forwarderContract.calculateMarketBuyResults.callAsync(
+ params.orders,
+ makerAssetFillAmount,
+ );
+ if (expectedMarketBuyFillResults.makerAssetFilledAmount.lessThan(makerAssetFillAmount)) {
+ throw new Error(INSUFFICENT_ORDERS_FOR_MAKER_AMOUNT);
+ }
+ fillAmountWei = expectedMarketBuyFillResults.takerAssetFilledAmount;
+ const expectedFeeAmount = expectedMarketBuyFillResults.takerFeePaid;
+ if (expectedFeeAmount.greaterThan(ZERO_AMOUNT)) {
+ const expectedFeeFillFillAmountWei = await this._calculateMarketBuyERC20FillAmountAsync(
+ feeOrders,
+ [],
+ DEFAULT_FEE_PROPORTION,
+ expectedFeeAmount,
+ );
+ fillAmountWei = fillAmountWei.plus(expectedFeeFillFillAmountWei);
+ }
+ }
+ fillAmountWei = ForwarderWrapper._calculateAdditionalFeeProportionAmount(feeProportion, fillAmountWei);
+ return fillAmountWei;
+ }
+ private async _calculateMarketBuyERC721FillAmountAsync(
+ orders: SignedOrder[],
+ feeOrders: SignedOrder[],
+ feeProportion: number,
+ ): Promise<BigNumber> {
+ // Total cost when buying ERC721 is the total cost of all ERC721 orders + any fee abstraction
+ let fillAmountWei = _.reduce(
+ orders,
+ (totalAmount: BigNumber, order: SignedOrder) => {
+ return totalAmount.plus(order.takerAssetAmount);
+ },
+ ZERO_AMOUNT,
+ );
+ const totalFees = _.reduce(
+ orders,
+ (totalAmount: BigNumber, order: SignedOrder) => {
+ return totalAmount.plus(order.takerFee);
+ },
+ ZERO_AMOUNT,
+ );
+ if (totalFees.greaterThan(ZERO_AMOUNT)) {
+ // Calculate the ZRX fee abstraction cost
+ const emptyFeeOrders: SignedOrder[] = [];
+ const expectedFeeAmountWei = await this._calculateMarketBuyERC20FillAmountAsync(
+ feeOrders,
+ emptyFeeOrders,
+ DEFAULT_FEE_PROPORTION,
+ totalFees,
+ );
+ fillAmountWei = fillAmountWei.plus(expectedFeeAmountWei);
+ }
+ fillAmountWei = ForwarderWrapper._calculateAdditionalFeeProportionAmount(feeProportion, fillAmountWei);
+ return fillAmountWei;
+ }
+}
diff --git a/packages/contracts/test/utils/order_factory_from_scenario.ts b/packages/contracts/test/utils/order_factory_from_scenario.ts
index 9670c1a59..526505871 100644
--- a/packages/contracts/test/utils/order_factory_from_scenario.ts
+++ b/packages/contracts/test/utils/order_factory_from_scenario.ts
@@ -2,7 +2,7 @@ import { assetProxyUtils, generatePseudoRandomSalt } from '@0xproject/order-util
import { Order } from '@0xproject/types';
import { BigNumber, errorUtils } from '@0xproject/utils';
-import { DummyERC721TokenContract } from '../../generated_contract_wrappers/dummy_e_r_c721_token';
+import { DummyERC721TokenContract } from '../../generated_contract_wrappers/dummy_erc721_token';
import { constants } from './constants';
import {
diff --git a/packages/contracts/test/utils/types.ts b/packages/contracts/test/utils/types.ts
index b792bb90a..67313b647 100644
--- a/packages/contracts/test/utils/types.ts
+++ b/packages/contracts/test/utils/types.ts
@@ -102,6 +102,7 @@ export enum ContractName {
TestWallet = 'TestWallet',
Authorizable = 'Authorizable',
Whitelist = 'Whitelist',
+ Forwarder = 'Forwarder',
}
export interface SignedTransaction {
@@ -227,3 +228,10 @@ export interface FillScenario {
makerStateScenario: TraderStateScenario;
takerStateScenario: TraderStateScenario;
}
+
+export interface FillResults {
+ makerAssetFilledAmount: BigNumber;
+ takerAssetFilledAmount: BigNumber;
+ makerFeePaid: BigNumber;
+ takerFeePaid: BigNumber;
+}