Files
protocol/packages/asset-swapper/test/forwarder_swap_quote_consumer_test.ts
Lawrence Forman 994908549d Asset-swapper aggregator utils (#2353)
* `@0x/asset-swapper`: Add ERC20Bridge aggregator library.

* `@0x/asset-swapper`: Finish off `aggregate.ts`.

* `@0x/types`: Add `OrderWithoutDomain` type.

* `@0x/asset-swapper`: Add testing infra for sampler/aggregator.

* `@0x/types`: Add `SignedOrderWithoutDomain` type.

* `@0x/asset-swapper`: Update aggregator to take and return orders with signatures.

* `@0x/asset-swapper`: Fix broken aggregator tests.

* `@0x/asset-swapper`: Pass the sampler contract into aggregator entry points.

* `@0x/contract-artifacts`: Add `IERC20BridgeSampler` artifact.

* `@0x/contract-wrappers`: Add `IERC20BridgeSampler` wrapper.

* `@0x/asset-swapper`: Address review comments.

* fixed testing

* refactored aggregate.ts and embeded into asset-swapper

* added adjusted rates for taker and maker fees

* remove PrunedSignedOrders

* updated contract-addresses and addressed some other todos

* streamlined logic

* patched in lawrences changes

* renamed aggregator utils and removed market_utils.ts

* added ack heartbeats

* fixed bug

* patches

* added dummy order things

* Dummy with valid sig

* Tweak gas price calculation to wei

* added test coverage and fixed bugs

* fixed migrations

* Fix CHANGELOGs and types export

* Deploy latest ERC20BridgeSampler on Mainnet

* `@0x/types` Revert CHANGELOG.

* `@0x/asset-swapper`: Address review comments.
`@0x/contract-addresses`: Make kyber lowercase.

* made protocol fee multiplier async

* `@0x/asset-swapper: Fix build errors and do some code cleanup.

* use assetDataUtils where possible
2019-12-16 12:35:58 -08:00

557 lines
26 KiB
TypeScript

import { ContractAddresses } from '@0x/contract-addresses';
import { ERC20TokenContract, ForwarderContract } from '@0x/contract-wrappers';
import { constants as devConstants, OrderFactory } from '@0x/contracts-test-utils';
import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils';
import { migrateOnceAsync } from '@0x/migrations';
import { assetDataUtils } from '@0x/order-utils';
import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import 'mocha';
import { SwapQuote } from '../src';
import { constants } from '../src/constants';
import { ForwarderSwapQuoteConsumer } from '../src/quote_consumers/forwarder_swap_quote_consumer';
import {
ForwarderMarketBuySmartContractParams,
ForwarderMarketSellSmartContractParams,
MarketBuySwapQuote,
MarketOperation,
SignedOrderWithFillableAmounts,
} from '../src/types';
import { ProtocolFeeUtils } from '../src/utils/protocol_fee_utils';
import { chaiSetup } from './utils/chai_setup';
import { getFullyFillableSwapQuoteWithNoFeesAsync } from './utils/swap_quote';
import { provider, web3Wrapper } from './utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
const GAS_PRICE = new BigNumber(devConstants.DEFAULT_GAS_PRICE);
const ONE_ETH_IN_WEI = new BigNumber(1000000000000000000);
const TESTRPC_CHAIN_ID = devConstants.TESTRPC_CHAIN_ID;
const UNLIMITED_ALLOWANCE_IN_BASE_UNITS = new BigNumber(2).pow(256).minus(1); // tslint:disable-line:custom-no-magic-numbers
const FEE_PERCENTAGE = 0.05;
const PARTIAL_PRUNED_SIGNED_ORDERS_FEELESS: Array<Partial<SignedOrderWithFillableAmounts>> = [
{
takerAssetAmount: new BigNumber(2).multipliedBy(ONE_ETH_IN_WEI),
makerAssetAmount: new BigNumber(2).multipliedBy(ONE_ETH_IN_WEI),
fillableTakerAssetAmount: new BigNumber(2).multipliedBy(ONE_ETH_IN_WEI),
fillableMakerAssetAmount: new BigNumber(2).multipliedBy(ONE_ETH_IN_WEI),
},
{
takerAssetAmount: new BigNumber(1).multipliedBy(ONE_ETH_IN_WEI),
makerAssetAmount: new BigNumber(3).multipliedBy(ONE_ETH_IN_WEI),
fillableTakerAssetAmount: new BigNumber(1).multipliedBy(ONE_ETH_IN_WEI),
fillableMakerAssetAmount: new BigNumber(3).multipliedBy(ONE_ETH_IN_WEI),
},
{
takerAssetAmount: new BigNumber(1).multipliedBy(ONE_ETH_IN_WEI),
makerAssetAmount: new BigNumber(5).multipliedBy(ONE_ETH_IN_WEI),
fillableTakerAssetAmount: new BigNumber(1).multipliedBy(ONE_ETH_IN_WEI),
fillableMakerAssetAmount: new BigNumber(5).multipliedBy(ONE_ETH_IN_WEI),
},
];
const expectMakerAndTakerBalancesAsyncFactory = (
erc20TokenContract: ERC20TokenContract,
makerAddress: string,
takerAddress: string,
) => async (expectedMakerBalance: BigNumber, expectedTakerBalance: BigNumber) => {
const makerBalance = await erc20TokenContract.balanceOf(makerAddress).callAsync();
const takerBalance = await erc20TokenContract.balanceOf(takerAddress).callAsync();
expect(makerBalance).to.bignumber.equal(expectedMakerBalance);
expect(takerBalance).to.bignumber.equal(expectedTakerBalance);
};
describe('ForwarderSwapQuoteConsumer', () => {
let protocolFeeUtils: ProtocolFeeUtils;
let userAddresses: string[];
let coinbaseAddress: string;
let makerAddress: string;
let takerAddress: string;
let feeRecipient: string;
let makerTokenAddress: string;
let takerTokenAddress: string;
let makerAssetData: string;
let takerAssetData: string;
let orderFactory: OrderFactory;
let invalidOrderFactory: OrderFactory;
let wethAssetData: string;
let contractAddresses: ContractAddresses;
let erc20TokenContract: ERC20TokenContract;
let forwarderContract: ForwarderContract;
let orders: SignedOrderWithFillableAmounts[];
let invalidOrders: SignedOrderWithFillableAmounts[];
let marketSellSwapQuote: SwapQuote;
let marketBuySwapQuote: SwapQuote;
let invalidMarketBuySwapQuote: SwapQuote;
let swapQuoteConsumer: ForwarderSwapQuoteConsumer;
let expectMakerAndTakerBalancesAsync: (
expectedMakerBalance: BigNumber,
expectedTakerBalance: BigNumber,
) => Promise<void>;
const chainId = TESTRPC_CHAIN_ID;
before(async () => {
contractAddresses = await migrateOnceAsync(provider);
await blockchainLifecycle.startAsync();
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
[coinbaseAddress, takerAddress, makerAddress, feeRecipient] = userAddresses;
[makerTokenAddress, takerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses();
erc20TokenContract = new ERC20TokenContract(makerTokenAddress, provider);
forwarderContract = new ForwarderContract(contractAddresses.forwarder, provider);
[makerAssetData, takerAssetData, wethAssetData] = [
assetDataUtils.encodeERC20AssetData(makerTokenAddress),
assetDataUtils.encodeERC20AssetData(takerTokenAddress),
assetDataUtils.encodeERC20AssetData(contractAddresses.etherToken),
];
// Configure order defaults
const defaultOrderParams = {
...devConstants.STATIC_ORDER_PARAMS,
makerAddress,
takerAddress: constants.NULL_ADDRESS,
makerAssetData,
takerAssetData: wethAssetData,
makerFeeAssetData: constants.NULL_ERC20_ASSET_DATA,
takerFeeAssetData: constants.NULL_ERC20_ASSET_DATA,
makerFee: constants.ZERO_AMOUNT,
takerFee: constants.ZERO_AMOUNT,
feeRecipientAddress: feeRecipient,
exchangeAddress: contractAddresses.exchange,
chainId,
};
const invalidDefaultOrderParams = {
...defaultOrderParams,
...{
takerAssetData,
},
};
const privateKey = devConstants.TESTRPC_PRIVATE_KEYS[userAddresses.indexOf(makerAddress)];
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
protocolFeeUtils = new ProtocolFeeUtils(constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS);
expectMakerAndTakerBalancesAsync = expectMakerAndTakerBalancesAsyncFactory(
erc20TokenContract,
makerAddress,
takerAddress,
);
invalidOrderFactory = new OrderFactory(privateKey, invalidDefaultOrderParams);
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
const UNLIMITED_ALLOWANCE = UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
const totalFillableAmount = new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI);
await erc20TokenContract.transfer(makerAddress, totalFillableAmount).sendTransactionAsync({
from: coinbaseAddress,
});
await erc20TokenContract
.approve(contractAddresses.erc20Proxy, UNLIMITED_ALLOWANCE)
.sendTransactionAsync({ from: makerAddress });
await forwarderContract.approveMakerAssetProxy(makerAssetData).sendTransactionAsync({ from: makerAddress });
orders = [];
for (const partialOrder of PARTIAL_PRUNED_SIGNED_ORDERS_FEELESS) {
const order = await orderFactory.newSignedOrderAsync(partialOrder);
const prunedOrder = {
...order,
...partialOrder,
};
orders.push(prunedOrder as SignedOrderWithFillableAmounts);
}
invalidOrders = [];
for (const partialOrder of PARTIAL_PRUNED_SIGNED_ORDERS_FEELESS) {
const order = await invalidOrderFactory.newSignedOrderAsync(partialOrder);
const prunedOrder = {
...order,
...partialOrder,
};
invalidOrders.push(prunedOrder as SignedOrderWithFillableAmounts);
}
marketSellSwapQuote = await getFullyFillableSwapQuoteWithNoFeesAsync(
makerAssetData,
wethAssetData,
orders,
MarketOperation.Sell,
GAS_PRICE,
protocolFeeUtils,
);
marketBuySwapQuote = await getFullyFillableSwapQuoteWithNoFeesAsync(
makerAssetData,
wethAssetData,
orders,
MarketOperation.Buy,
GAS_PRICE,
protocolFeeUtils,
);
invalidMarketBuySwapQuote = await getFullyFillableSwapQuoteWithNoFeesAsync(
makerAssetData,
takerAssetData,
invalidOrders,
MarketOperation.Buy,
GAS_PRICE,
protocolFeeUtils,
);
swapQuoteConsumer = new ForwarderSwapQuoteConsumer(provider, contractAddresses, {
chainId,
});
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('#executeSwapQuoteOrThrowAsync', () => {
describe('validation', () => {
it('should throw if swapQuote provided is not a valid forwarder SwapQuote (taker asset is wEth)', async () => {
expect(
swapQuoteConsumer.executeSwapQuoteOrThrowAsync(invalidMarketBuySwapQuote, { takerAddress }),
).to.be.rejectedWith(
`Expected quote.orders[0] to have takerAssetData set as ${wethAssetData}, but is ${takerAssetData}`,
);
});
});
// TODO(david) test execution of swap quotes with fee orders
describe('valid swap quote', () => {
/*
* Testing that SwapQuoteConsumer logic correctly performs a execution (doesn't throw or revert)
* Does not test the validity of the state change performed by the forwarder smart contract
*/
it('should perform a marketBuy execution when provided a MarketBuy type swapQuote', async () => {
await expectMakerAndTakerBalancesAsync(
new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI),
constants.ZERO_AMOUNT,
);
await swapQuoteConsumer.executeSwapQuoteOrThrowAsync(marketBuySwapQuote, {
takerAddress,
gasLimit: 4000000,
ethAmount: new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI),
});
await expectMakerAndTakerBalancesAsync(
constants.ZERO_AMOUNT,
new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI),
);
});
it('should perform a marketSell execution when provided a MarketSell type swapQuote', async () => {
await expectMakerAndTakerBalancesAsync(
new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI),
constants.ZERO_AMOUNT,
);
await swapQuoteConsumer.executeSwapQuoteOrThrowAsync(marketSellSwapQuote, {
takerAddress,
gasLimit: 4000000,
});
await expectMakerAndTakerBalancesAsync(
constants.ZERO_AMOUNT,
new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI),
);
});
it('should perform a marketBuy execution with affiliate fees', async () => {
await expectMakerAndTakerBalancesAsync(
new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI),
constants.ZERO_AMOUNT,
);
const feeRecipientEthBalanceBefore = await web3Wrapper.getBalanceInWeiAsync(feeRecipient);
await swapQuoteConsumer.executeSwapQuoteOrThrowAsync(marketBuySwapQuote, {
takerAddress,
gasLimit: 4000000,
extensionContractOpts: {
feePercentage: 0.05,
feeRecipient,
},
});
await expectMakerAndTakerBalancesAsync(
constants.ZERO_AMOUNT,
new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI),
);
const feeRecipientEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(feeRecipient);
const totalEthSpent = marketBuySwapQuote.bestCaseQuoteInfo.totalTakerAssetAmount.plus(
marketBuySwapQuote.bestCaseQuoteInfo.protocolFeeInWeiAmount,
);
expect(feeRecipientEthBalanceAfter.minus(feeRecipientEthBalanceBefore)).to.bignumber.equal(
new BigNumber(FEE_PERCENTAGE).times(totalEthSpent),
);
});
it('should perform a marketSell execution with affiliate fees', async () => {
await expectMakerAndTakerBalancesAsync(
new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI),
constants.ZERO_AMOUNT,
);
const feeRecipientEthBalanceBefore = await web3Wrapper.getBalanceInWeiAsync(feeRecipient);
await swapQuoteConsumer.executeSwapQuoteOrThrowAsync(marketSellSwapQuote, {
takerAddress,
gasLimit: 4000000,
extensionContractOpts: {
feePercentage: 0.05,
feeRecipient,
},
});
await expectMakerAndTakerBalancesAsync(
constants.ZERO_AMOUNT,
new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI),
);
const feeRecipientEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(feeRecipient);
const totalEthSpent = marketBuySwapQuote.bestCaseQuoteInfo.totalTakerAssetAmount.plus(
marketBuySwapQuote.bestCaseQuoteInfo.protocolFeeInWeiAmount,
);
expect(feeRecipientEthBalanceAfter.minus(feeRecipientEthBalanceBefore)).to.bignumber.equal(
new BigNumber(FEE_PERCENTAGE).times(totalEthSpent),
);
});
});
});
describe('#getSmartContractParamsOrThrow', () => {
describe('validation', () => {
it('should throw if swap quote provided is not a valid forwarder SwapQuote (taker asset is WETH)', async () => {
expect(
swapQuoteConsumer.getSmartContractParamsOrThrowAsync(invalidMarketBuySwapQuote, {}),
).to.be.rejectedWith(
`Expected quote.orders[0] to have takerAssetData set as ${wethAssetData}, but is ${takerAssetData}`,
);
});
});
describe('valid swap quote', async () => {
it('provide correct and optimized smart contract params with default options for a marketSell SwapQuote (no affiliate fees)', async () => {
const { toAddress, params } = await swapQuoteConsumer.getSmartContractParamsOrThrowAsync(
marketSellSwapQuote,
{},
);
expect(toAddress).to.deep.equal(forwarderContract.address);
const {
feePercentage,
feeRecipient: feeRecipientFromParams,
signatures,
type,
} = params as ForwarderMarketSellSmartContractParams;
expect(type).to.deep.equal(MarketOperation.Sell);
expect(feeRecipientFromParams).to.deep.equal(constants.NULL_ADDRESS);
const orderSignatures = marketSellSwapQuote.orders.map(order => order.signature);
expect(signatures).to.deep.equal(orderSignatures);
expect(feePercentage).to.bignumber.equal(0);
});
it('provide correct and optimized smart contract params with default options for a marketBuy SwapQuote (no affiliate fees)', async () => {
const { toAddress, params } = await swapQuoteConsumer.getSmartContractParamsOrThrowAsync(
marketBuySwapQuote,
{},
);
expect(toAddress).to.deep.equal(forwarderContract.address);
const {
makerAssetFillAmount,
feePercentage,
feeRecipient: feeRecipientFromParams,
signatures,
type,
} = params as ForwarderMarketBuySmartContractParams;
expect(type).to.deep.equal(MarketOperation.Buy);
expect(feeRecipientFromParams).to.deep.equal(constants.NULL_ADDRESS);
expect(makerAssetFillAmount).to.bignumber.equal(
(marketBuySwapQuote as MarketBuySwapQuote).makerAssetFillAmount,
);
const orderSignatures = marketBuySwapQuote.orders.map(order => order.signature);
expect(signatures).to.deep.equal(orderSignatures);
expect(feePercentage).to.bignumber.equal(0);
});
it('provide correct and optimized smart contract params with affiliate fees for a marketSell SwapQuote', async () => {
const { toAddress, params } = await swapQuoteConsumer.getSmartContractParamsOrThrowAsync(
marketSellSwapQuote,
{
extensionContractOpts: {
feePercentage: 0.05,
feeRecipient,
},
},
);
expect(toAddress).to.deep.equal(forwarderContract.address);
const {
feePercentage,
feeRecipient: feeRecipientFromParams,
signatures,
type,
} = params as ForwarderMarketSellSmartContractParams;
expect(type).to.deep.equal(MarketOperation.Sell);
expect(feeRecipientFromParams).to.deep.equal(feeRecipient);
const orderSignatures = marketSellSwapQuote.orders.map(order => order.signature);
expect(signatures).to.deep.equal(orderSignatures);
expect(feePercentage).to.bignumber.equal(new BigNumber(0.05).multipliedBy(ONE_ETH_IN_WEI));
});
it('provide correct and optimized smart contract params with affiliate fees for a marketBuy SwapQuote', async () => {
const { toAddress, params } = await swapQuoteConsumer.getSmartContractParamsOrThrowAsync(
marketBuySwapQuote,
{
extensionContractOpts: {
feePercentage: 0.05,
feeRecipient,
},
},
);
expect(toAddress).to.deep.equal(forwarderContract.address);
const {
makerAssetFillAmount,
feePercentage,
feeRecipient: feeRecipientFromParams,
signatures,
type,
} = params as ForwarderMarketBuySmartContractParams;
expect(type).to.deep.equal(MarketOperation.Buy);
expect(feeRecipientFromParams).to.deep.equal(feeRecipient);
expect(makerAssetFillAmount).to.bignumber.equal(
(marketBuySwapQuote as MarketBuySwapQuote).makerAssetFillAmount,
);
const orderSignatures = marketBuySwapQuote.orders.map(order => order.signature);
expect(signatures).to.deep.equal(orderSignatures);
expect(feePercentage).to.bignumber.equal(new BigNumber(0.05).multipliedBy(ONE_ETH_IN_WEI));
});
});
});
describe('#getCalldataOrThrow', () => {
describe('validation', () => {
it('should throw if swap quote provided is not a valid forwarder SwapQuote (taker asset is WETH)', async () => {
expect(swapQuoteConsumer.getCalldataOrThrowAsync(invalidMarketBuySwapQuote, {})).to.be.rejectedWith(
`Expected quote.orders[0] to have takerAssetData set as ${wethAssetData}, but is ${takerAssetData}`,
);
});
});
describe('valid swap quote', async () => {
it('provide correct and optimized calldata options with default options for a marketSell SwapQuote (no affiliate fees)', async () => {
await expectMakerAndTakerBalancesAsync(
new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI),
constants.ZERO_AMOUNT,
);
const { calldataHexString, toAddress, ethAmount } = await swapQuoteConsumer.getCalldataOrThrowAsync(
marketSellSwapQuote,
{},
);
expect(toAddress).to.deep.equal(forwarderContract.address);
await web3Wrapper.sendTransactionAsync({
from: takerAddress,
to: toAddress,
data: calldataHexString,
value: ethAmount,
gasPrice: GAS_PRICE,
gas: 4000000,
});
await expectMakerAndTakerBalancesAsync(
constants.ZERO_AMOUNT,
new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI),
);
});
it('provide correct and optimized calldata options with default options for a marketBuy SwapQuote (no affiliate fees)', async () => {
await expectMakerAndTakerBalancesAsync(
new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI),
constants.ZERO_AMOUNT,
);
const { calldataHexString, toAddress, ethAmount } = await swapQuoteConsumer.getCalldataOrThrowAsync(
marketBuySwapQuote,
{},
);
expect(toAddress).to.deep.equal(contractAddresses.forwarder);
await web3Wrapper.sendTransactionAsync({
from: takerAddress,
to: toAddress,
data: calldataHexString,
value: ethAmount,
gasPrice: GAS_PRICE,
gas: 4000000,
});
await expectMakerAndTakerBalancesAsync(
constants.ZERO_AMOUNT,
new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI),
);
});
it('provide correct and optimized calldata options with affiliate fees for a marketSell SwapQuote', async () => {
await expectMakerAndTakerBalancesAsync(
new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI),
constants.ZERO_AMOUNT,
);
const feeRecipientEthBalanceBefore = await web3Wrapper.getBalanceInWeiAsync(feeRecipient);
const { calldataHexString, toAddress, ethAmount } = await swapQuoteConsumer.getCalldataOrThrowAsync(
marketSellSwapQuote,
{
extensionContractOpts: {
feePercentage: 0.05,
feeRecipient,
},
},
);
expect(toAddress).to.deep.equal(contractAddresses.forwarder);
await web3Wrapper.sendTransactionAsync({
from: takerAddress,
to: toAddress,
data: calldataHexString,
value: ethAmount,
gasPrice: GAS_PRICE,
gas: 4000000,
});
await expectMakerAndTakerBalancesAsync(
constants.ZERO_AMOUNT,
new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI),
);
const totalEthSpent = marketBuySwapQuote.bestCaseQuoteInfo.totalTakerAssetAmount.plus(
marketBuySwapQuote.bestCaseQuoteInfo.protocolFeeInWeiAmount,
);
const feeRecipientEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(feeRecipient);
expect(feeRecipientEthBalanceAfter.minus(feeRecipientEthBalanceBefore)).to.bignumber.equal(
new BigNumber(FEE_PERCENTAGE).times(totalEthSpent),
);
});
it('provide correct and optimized calldata options with affiliate fees for a marketBuy SwapQuote', async () => {
await expectMakerAndTakerBalancesAsync(
new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI),
constants.ZERO_AMOUNT,
);
const feeRecipientEthBalanceBefore = await web3Wrapper.getBalanceInWeiAsync(feeRecipient);
const { calldataHexString, toAddress, ethAmount } = await swapQuoteConsumer.getCalldataOrThrowAsync(
marketBuySwapQuote,
{
extensionContractOpts: {
feePercentage: 0.05,
feeRecipient,
},
},
);
expect(toAddress).to.deep.equal(contractAddresses.forwarder);
await web3Wrapper.sendTransactionAsync({
from: takerAddress,
to: toAddress,
data: calldataHexString,
value: ethAmount,
gasPrice: GAS_PRICE,
gas: 4000000,
});
await expectMakerAndTakerBalancesAsync(
constants.ZERO_AMOUNT,
new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI),
);
const totalEthSpent = marketBuySwapQuote.bestCaseQuoteInfo.totalTakerAssetAmount.plus(
marketBuySwapQuote.bestCaseQuoteInfo.protocolFeeInWeiAmount,
);
const feeRecipientEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(feeRecipient);
expect(feeRecipientEthBalanceAfter.minus(feeRecipientEthBalanceBefore)).to.bignumber.equal(
new BigNumber(FEE_PERCENTAGE).times(totalEthSpent),
);
});
});
});
// tslint:disable-next-line: max-file-line-count
});