Finished up adding test coverage

This commit is contained in:
David Sun 2019-07-25 14:07:32 -07:00
parent db1e9769d0
commit c8d0ff846c
7 changed files with 304 additions and 148 deletions

View File

@ -58,7 +58,7 @@ export class ExchangeSwapQuoteConsumer implements SwapQuoteConsumerBase<Exchange
const { takerAssetFillAmount } = params; const { takerAssetFillAmount } = params;
args = [orders, takerAssetFillAmount, signatures]; args = [orders, takerAssetFillAmount, signatures];
} }
const calldataHexString = abiEncoder.encode(args); const calldataHexString = abiEncoder.encode(args, { shouldOptimize: true });
return { return {
calldataHexString, calldataHexString,
methodAbi, methodAbi,

View File

@ -56,7 +56,6 @@ export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumerBase<Forward
const { to, methodAbi, ethAmount, params } = await this.getSmartContractParamsOrThrowAsync(quote, opts); const { to, methodAbi, ethAmount, params } = await this.getSmartContractParamsOrThrowAsync(quote, opts);
const abiEncoder = new AbiEncoder.Method(methodAbi); const abiEncoder = new AbiEncoder.Method(methodAbi);
const { orders, signatures, feeOrders, feeSignatures, feePercentage, feeRecipient } = params; const { orders, signatures, feeOrders, feeSignatures, feePercentage, feeRecipient } = params;
let args: any[]; let args: any[];
@ -66,7 +65,7 @@ export class ForwarderSwapQuoteConsumer implements SwapQuoteConsumerBase<Forward
} else { } else {
args = [orders, signatures, feeOrders, feeSignatures, feePercentage, feeRecipient]; args = [orders, signatures, feeOrders, feeSignatures, feePercentage, feeRecipient];
} }
const calldataHexString = abiEncoder.encode(args); const calldataHexString = abiEncoder.encode(args, { shouldOptimize: true });
return { return {
calldataHexString, calldataHexString,
methodAbi, methodAbi,

View File

@ -35,11 +35,11 @@ export const assert = {
_.every(orders, (order: SignedOrder, index: number) => { _.every(orders, (order: SignedOrder, index: number) => {
assert.assert( assert.assert(
order.takerAssetData === takerAssetData, order.takerAssetData === takerAssetData,
`Expected ${variableName}[${index}].takerAssetData to be ${takerAssetData}`, `Expected ${variableName}[${index}].takerAssetData to be ${takerAssetData} but found ${order.takerAssetData}`,
); );
assert.assert( assert.assert(
order.makerAssetData === makerAssetData, order.makerAssetData === makerAssetData,
`Expected ${variableName}[${index}].makerAssetData to be ${makerAssetData}`, `Expected ${variableName}[${index}].makerAssetData to be ${makerAssetData} but found ${order.makerAssetData}`,
); );
}); });
}, },

View File

@ -84,7 +84,7 @@ export const swapQuoteConsumerUtils = {
return optimizedOrder; return optimizedOrder;
}); });
}, },
async getConsumerForSwapQuoteAsync( async getConsumerTypeForSwapQuoteAsync(
quote: SwapQuote, quote: SwapQuote,
contractWrappers: ContractWrappers, contractWrappers: ContractWrappers,
provider: Provider, provider: Provider,

View File

@ -8,10 +8,11 @@ import { BigNumber } from '@0x/utils';
import * as chai from 'chai'; import * as chai from 'chai';
import 'mocha'; import 'mocha';
import { SwapQuote } from '../src'; import { SwapQuote, SwapQuoteConsumer } from '../src';
import { constants } from '../src/constants';
import { ExchangeSwapQuoteConsumer } from '../src/quote_consumers/exchange_swap_quote_consumer'; import { ExchangeSwapQuoteConsumer } from '../src/quote_consumers/exchange_swap_quote_consumer';
import { ForwarderSwapQuoteConsumer } from '../src/quote_consumers/forwarder_swap_quote_consumer'; import { ForwarderSwapQuoteConsumer } from '../src/quote_consumers/forwarder_swap_quote_consumer';
import { constants } from '../src/constants'; import { ExchangeMarketBuySmartContractParams, ExchangeMarketSellSmartContractParams, MarketBuySwapQuote, MarketSellSwapQuote } from '../src/types';
import { chaiSetup } from './utils/chai_setup'; import { chaiSetup } from './utils/chai_setup';
import { migrateOnceAsync } from './utils/migrate'; import { migrateOnceAsync } from './utils/migrate';
@ -44,6 +45,12 @@ describe('ExchangeSwapQuoteConsumer', () => {
let contractAddresses: ContractAddresses; let contractAddresses: ContractAddresses;
const networkId = TESTRPC_NETWORK_ID; const networkId = TESTRPC_NETWORK_ID;
let orders: SignedOrder[];
let marketSellSwapQuote: SwapQuote;
let marketBuySwapQuote: SwapQuote;
let swapQuoteConsumer: ExchangeSwapQuoteConsumer;
before(async () => { before(async () => {
contractAddresses = await migrateOnceAsync(); contractAddresses = await migrateOnceAsync();
await blockchainLifecycle.startAsync(); await blockchainLifecycle.startAsync();
@ -74,19 +81,7 @@ describe('ExchangeSwapQuoteConsumer', () => {
}); });
beforeEach(async () => { beforeEach(async () => {
await blockchainLifecycle.startAsync(); await blockchainLifecycle.startAsync();
}); orders = [];
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('executeSwapQuoteOrThrowAsync', () => {
describe('valid swap quote', () => {
const orders: SignedOrder[] = [];
let marketSellSwapQuote: SwapQuote;
let marketBuySwapQuote: SwapQuote;
let swapQuoteConsumer: ExchangeSwapQuoteConsumer;
beforeEach(async () => {
for (const fillableAmmount of FILLABLE_AMOUNTS) { for (const fillableAmmount of FILLABLE_AMOUNTS) {
const order = await fillScenarios.createFillableSignedOrderAsync( const order = await fillScenarios.createFillableSignedOrderAsync(
makerAssetData, makerAssetData,
@ -100,14 +95,14 @@ describe('ExchangeSwapQuoteConsumer', () => {
marketSellSwapQuote = getFullyFillableSwapQuoteWithNoFees( marketSellSwapQuote = getFullyFillableSwapQuoteWithNoFees(
makerAssetData, makerAssetData,
wethAssetData, takerAssetData,
orders, orders,
MarketOperation.Sell, MarketOperation.Sell,
); );
marketBuySwapQuote = getFullyFillableSwapQuoteWithNoFees( marketBuySwapQuote = getFullyFillableSwapQuoteWithNoFees(
makerAssetData, makerAssetData,
wethAssetData, takerAssetData,
orders, orders,
MarketOperation.Buy, MarketOperation.Buy,
); );
@ -116,6 +111,11 @@ describe('ExchangeSwapQuoteConsumer', () => {
networkId, networkId,
}); });
}); });
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('executeSwapQuoteOrThrowAsync', () => {
/* /*
* Testing that SwapQuoteConsumer logic correctly performs a execution (doesn't throw or revert) * 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 * Does not test the validity of the state change performed by the forwarder smart contract
@ -143,32 +143,69 @@ describe('ExchangeSwapQuoteConsumer', () => {
expect(makerBalance).to.bignumber.equal(constants.ZERO_AMOUNT); expect(makerBalance).to.bignumber.equal(constants.ZERO_AMOUNT);
}); });
}); });
});
describe('getSmartContractParamsOrThrow', () => { describe('getSmartContractParamsOrThrow', () => {
describe('valid swap quote', async () => { describe('valid swap quote', async () => {
it('provide correct and optimized smart contract params with default options for a marketSell SwapQuote', async () => { // TODO(david) Check for valid MethodAbi
it('provide correct and optimized smart contract params for a marketSell SwapQuote', async () => {
const { to, params } = await swapQuoteConsumer.getSmartContractParamsOrThrowAsync(marketSellSwapQuote, {});
expect(to).to.deep.equal(contractWrappers.exchange.address);
const { takerAssetFillAmount, signatures, type } = params as ExchangeMarketSellSmartContractParams;
expect(type).to.deep.equal(MarketOperation.Sell);
expect(takerAssetFillAmount).to.bignumber.equal((marketSellSwapQuote as MarketSellSwapQuote).takerAssetFillAmount);
const orderSignatures = marketSellSwapQuote.orders.map(order => order.signature);
expect(signatures).to.deep.equal(orderSignatures);
}); });
it('provide correct and optimized smart contract params with default options for a marketBuy SwapQuote', async () => { it('provide correct and optimized smart contract params for a marketBuy SwapQuote', async () => {
const { to, params } = await swapQuoteConsumer.getSmartContractParamsOrThrowAsync(marketBuySwapQuote, {});
expect(to).to.deep.equal(contractWrappers.exchange.address);
const { makerAssetFillAmount, signatures, type } = params as ExchangeMarketBuySmartContractParams;
expect(type).to.deep.equal(MarketOperation.Buy);
expect(makerAssetFillAmount).to.bignumber.equal((marketBuySwapQuote as MarketBuySwapQuote).makerAssetFillAmount);
const orderSignatures = marketSellSwapQuote.orders.map(order => order.signature);
expect(signatures).to.deep.equal(orderSignatures);
}); });
}); });
}); });
describe('getCalldataOrThrow', () => { describe('getCalldataOrThrow', () => {
describe('validation', () => {
it('should throw if swap quote provided is not a valid forwarder SwapQuote (taker asset is WETH)', async () => {
});
});
describe('valid swap quote', async () => { describe('valid swap quote', async () => {
it('provide correct and optimized calldata options with default options for a marketSell SwapQuote (no affiliate fees)', async () => { it('provide correct and optimized calldata options with default options for a marketSell SwapQuote (no affiliate fees)', async () => {
let makerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, makerAddress);
let takerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, takerAddress);
expect(makerBalance).to.bignumber.equal((new BigNumber(10)).multipliedBy(ONE_ETH_IN_WEI));
expect(takerBalance).to.bignumber.equal(constants.ZERO_AMOUNT);
const { calldataHexString, to } = await swapQuoteConsumer.getCalldataOrThrowAsync(marketSellSwapQuote, {});
expect(to).to.deep.equal(contractWrappers.exchange.address);
await web3Wrapper.sendTransactionAsync({
from: takerAddress,
to,
data: calldataHexString,
gas: 4000000,
});
makerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, makerAddress);
takerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, takerAddress);
expect(takerBalance).to.bignumber.equal((new BigNumber(10)).multipliedBy(ONE_ETH_IN_WEI));
expect(makerBalance).to.bignumber.equal(constants.ZERO_AMOUNT);
}); });
it('provide correct and optimized calldata options with default options for a marketBuy SwapQuote (no affiliate fees)', async () => { it('provide correct and optimized calldata options with default options for a marketBuy SwapQuote (no affiliate fees)', async () => {
let makerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, makerAddress);
let takerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, takerAddress);
expect(makerBalance).to.bignumber.equal((new BigNumber(10)).multipliedBy(ONE_ETH_IN_WEI));
expect(takerBalance).to.bignumber.equal(constants.ZERO_AMOUNT);
const { calldataHexString, to } = await swapQuoteConsumer.getCalldataOrThrowAsync(marketBuySwapQuote, {});
expect(to).to.deep.equal(contractWrappers.exchange.address);
await web3Wrapper.sendTransactionAsync({
from: takerAddress,
to,
data: calldataHexString,
gas: 4000000,
}); });
it('provide correct and optimized calldata options with affiliate fees for a marketSell SwapQuote', async () => { makerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, makerAddress);
}); takerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, takerAddress);
it('provide correct and optimized calldata options with affiliate fees for a marketBuy SwapQuote', async () => { expect(takerBalance).to.bignumber.equal((new BigNumber(10)).multipliedBy(ONE_ETH_IN_WEI));
expect(makerBalance).to.bignumber.equal(constants.ZERO_AMOUNT);
}); });
}); });
}); });

View File

@ -4,7 +4,7 @@ import { BlockchainLifecycle } from '@0x/dev-utils';
import { FillScenarios } from '@0x/fill-scenarios'; import { FillScenarios } from '@0x/fill-scenarios';
import { assetDataUtils } from '@0x/order-utils'; import { assetDataUtils } from '@0x/order-utils';
import { MarketOperation, SignedOrder } from '@0x/types'; import { MarketOperation, SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils'; import { BigNumber, AbiDecoder } from '@0x/utils';
import * as chai from 'chai'; import * as chai from 'chai';
import 'mocha'; import 'mocha';
@ -15,6 +15,8 @@ import { chaiSetup } from './utils/chai_setup';
import { migrateOnceAsync } from './utils/migrate'; import { migrateOnceAsync } from './utils/migrate';
import { getFullyFillableSwapQuoteWithNoFees, getSignedOrdersWithNoFeesAsync } from './utils/swap_quote'; import { getFullyFillableSwapQuoteWithNoFees, getSignedOrdersWithNoFeesAsync } from './utils/swap_quote';
import { provider, web3Wrapper } from './utils/web3_wrapper'; import { provider, web3Wrapper } from './utils/web3_wrapper';
import { constants } from '../src/constants';
import { ForwarderMarketSellSmartContractParams, MarketSellSwapQuote, ForwarderMarketBuySmartContractParams, MarketBuySwapQuote } from '../src/types';
chaiSetup.configure(); chaiSetup.configure();
const expect = chai.expect; const expect = chai.expect;
@ -41,6 +43,12 @@ describe('ForwarderSwapQuoteConsumer', () => {
let wethAssetData: string; let wethAssetData: string;
let contractAddresses: ContractAddresses; let contractAddresses: ContractAddresses;
let orders: SignedOrder[];
let marketSellSwapQuote: SwapQuote;
let marketBuySwapQuote: SwapQuote;
let swapQuoteConsumer: ForwarderSwapQuoteConsumer;
let erc20ProxyAddress: string;
const networkId = TESTRPC_NETWORK_ID; const networkId = TESTRPC_NETWORK_ID;
before(async () => { before(async () => {
contractAddresses = await migrateOnceAsync(); contractAddresses = await migrateOnceAsync();
@ -72,45 +80,6 @@ describe('ForwarderSwapQuoteConsumer', () => {
}); });
beforeEach(async () => { beforeEach(async () => {
await blockchainLifecycle.startAsync(); await blockchainLifecycle.startAsync();
});
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 () => {
const invalidSignedOrders = await getSignedOrdersWithNoFeesAsync(
provider,
makerAssetData,
takerAssetData,
makerAddress,
takerAddress,
FILLABLE_AMOUNTS,
);
const invalidSwapQuote = getFullyFillableSwapQuoteWithNoFees(
makerAssetData,
takerAssetData,
invalidSignedOrders,
MARKET_OPERATION,
);
const swapQuoteConsumer = new ForwarderSwapQuoteConsumer(provider, {
networkId,
});
expect(
swapQuoteConsumer.executeSwapQuoteOrThrowAsync(invalidSwapQuote, { takerAddress }),
).to.be.rejectedWith(`Expected quote.orders[0] to have takerAssetData set as ${wethAssetData}, but is ${takerAssetData}`);
});
});
describe('valid swap quote', () => {
let orders: SignedOrder[];
let marketSellSwapQuote: SwapQuote;
let marketBuySwapQuote: SwapQuote;
let swapQuoteConsumer: ForwarderSwapQuoteConsumer;
let erc20ProxyAddress: string;
beforeEach(async () => {
const UNLIMITED_ALLOWANCE = contractWrappers.erc20Token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; const UNLIMITED_ALLOWANCE = contractWrappers.erc20Token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
erc20ProxyAddress = contractWrappers.erc20Proxy.address; erc20ProxyAddress = contractWrappers.erc20Proxy.address;
@ -147,52 +116,95 @@ describe('ForwarderSwapQuoteConsumer', () => {
networkId, networkId,
}); });
}); });
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 () => {
const invalidSignedOrders = await getSignedOrdersWithNoFeesAsync(
provider,
makerAssetData,
takerAssetData,
makerAddress,
takerAddress,
FILLABLE_AMOUNTS,
);
const invalidSwapQuote = getFullyFillableSwapQuoteWithNoFees(
makerAssetData,
takerAssetData,
invalidSignedOrders,
MARKET_OPERATION,
);
expect(
swapQuoteConsumer.executeSwapQuoteOrThrowAsync(invalidSwapQuote, { 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) * 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 * Does not test the validity of the state change performed by the forwarder smart contract
*/ */
it('should perform a marketSell execution when provided a MarketSell type swapQuote', async () => { it('should perform a marketSell execution when provided a MarketSell type swapQuote', async () => {
// const makerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, makerAddress); let makerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, makerAddress);
// const takerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, takerAddress); let takerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, takerAddress);
// console.log(makerBalance, takerBalance); expect(makerBalance).to.bignumber.equal((new BigNumber(10)).multipliedBy(ONE_ETH_IN_WEI));
// const preEthBalanceMaker = await web3Wrapper.getBalanceInWeiAsync(makerAddress); expect(takerBalance).to.bignumber.equal(constants.ZERO_AMOUNT);
// const preEthBalanceTaker = await web3Wrapper.getBalanceInWeiAsync(takerAddress); await swapQuoteConsumer.executeSwapQuoteOrThrowAsync(marketSellSwapQuote, { takerAddress });
// console.log('maker eth balance', preEthBalanceMaker, 'taker eth balance', preEthBalanceTaker); makerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, makerAddress);
// const txHash = await swapQuoteConsumer.executeSwapQuoteOrThrowAsync(marketSellSwapQuote, { takerAddress }); takerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, takerAddress);
// console.log(txHash); expect(makerBalance).to.bignumber.equal((new BigNumber(0.5)).multipliedBy(ONE_ETH_IN_WEI));
// // expect( expect(takerBalance).to.bignumber.equal((new BigNumber(9.5)).multipliedBy(ONE_ETH_IN_WEI));
// // swapQuoteConsumer.executeSwapQuoteOrThrowAsync(marketSellSwapQuote, { takerAddress }),
// // ).to.not.be.rejected();
// const ethBalanceMaker = await web3Wrapper.getBalanceInWeiAsync(makerAddress);
// const ethBalanceTaker = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
// console.log('maker eth balance', ethBalanceMaker, 'taker eth balance', ethBalanceTaker);
// const postMakerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, makerAddress);
// const postTakerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, takerAddress);
// console.log(postMakerBalance, postTakerBalance);
});
it('should perform a marketBuy execution when provided a MarketBuy type swapQuote', async () => {
// const makerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, makerAddress);
// const takerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, takerAddress);
// console.log(makerBalance, takerBalance);
// const preEthBalanceMaker = await web3Wrapper.getBalanceInWeiAsync(makerAddress);
// const preEthBalanceTaker = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
// console.log('maker eth balance', preEthBalanceMaker, 'taker eth balance', preEthBalanceTaker);
// await swapQuoteConsumer.executeSwapQuoteOrThrowAsync(marketBuySwapQuote, { takerAddress });
// // expect(
// // swapQuoteConsumer.executeSwapQuoteOrThrowAsync(marketBuySwapQuote, { takerAddress }),
// // ).to.not.be.rejected();
// const ethBalanceMaker = await web3Wrapper.getBalanceInWeiAsync(makerAddress);
// const ethBalanceTaker = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
// console.log('maker eth balance', ethBalanceMaker, 'taker eth balance', ethBalanceTaker);
// const postMakerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, makerAddress);
// const postTakerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, takerAddress);
// console.log(postMakerBalance, postTakerBalance);
}); });
it('should perform a marketSell execution with affiliate fees', () => {}); it('should perform a marketBuy execution when provided a MarketBuy type swapQuote', async () => {
it('should perform a marketSell execution with provided ethAmount in options', () => {}); let makerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, makerAddress);
it('should throw on a marketSell execution with provided ethAmount that is lower than bestCaseQuoteInfo.totalTakerAssetAmount in options', () => {}); let takerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, takerAddress);
it('should perform a marketSell execution with provided takerAddress in options', () => {}); expect(makerBalance).to.bignumber.equal((new BigNumber(10)).multipliedBy(ONE_ETH_IN_WEI));
expect(takerBalance).to.bignumber.equal(constants.ZERO_AMOUNT);
await swapQuoteConsumer.executeSwapQuoteOrThrowAsync(marketBuySwapQuote, { takerAddress });
makerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, makerAddress);
takerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, takerAddress);
expect(takerBalance).to.bignumber.equal((new BigNumber(10)).multipliedBy(ONE_ETH_IN_WEI));
expect(makerBalance).to.bignumber.equal(constants.ZERO_AMOUNT);
});
it('should perform a marketBuy execution with affiliate fees', async () => {
let makerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, makerAddress);
let takerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, takerAddress);
const feeRecipientEthBalanceBefore = await web3Wrapper.getBalanceInWeiAsync(feeRecipient);
expect(makerBalance).to.bignumber.equal((new BigNumber(10)).multipliedBy(ONE_ETH_IN_WEI));
expect(takerBalance).to.bignumber.equal(constants.ZERO_AMOUNT);
await swapQuoteConsumer.executeSwapQuoteOrThrowAsync(marketBuySwapQuote, { takerAddress, feePercentage: 0.05, feeRecipient });
makerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, makerAddress);
takerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, takerAddress);
const feeRecipientEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(feeRecipient);
expect(makerBalance).to.bignumber.equal(constants.ZERO_AMOUNT);
expect(takerBalance).to.bignumber.equal((new BigNumber(10)).multipliedBy(ONE_ETH_IN_WEI));
expect(feeRecipientEthBalanceAfter.minus(feeRecipientEthBalanceBefore)).to.bignumber.equal((new BigNumber(0.5)).multipliedBy(ONE_ETH_IN_WEI));
});
// TODO(david) Finish marketSell affiliate fee excution testing
// it('should perform a marketSell execution with affiliate fees', async () => {
// let makerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, makerAddress);
// let takerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, takerAddress);
// const feeRecipientEthBalanceBefore = await web3Wrapper.getBalanceInWeiAsync(feeRecipient);
// expect(makerBalance).to.bignumber.equal((new BigNumber(10)).multipliedBy(ONE_ETH_IN_WEI));
// expect(takerBalance).to.bignumber.equal(constants.ZERO_AMOUNT);
// console.log(makerBalance, takerBalance, feeRecipientEthBalanceBefore);
// await swapQuoteConsumer.executeSwapQuoteOrThrowAsync(marketSellSwapQuote, { takerAddress, feePercentage: 0.05, feeRecipient });
// makerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, makerAddress);
// takerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, takerAddress);
// const feeRecipientEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(feeRecipient);
// console.log(makerBalance, takerBalance, feeRecipientEthBalanceAfter);
// expect(makerBalance).to.bignumber.equal((new BigNumber(0.5)).multipliedBy(ONE_ETH_IN_WEI));
// expect(takerBalance).to.bignumber.equal((new BigNumber(9.5)).multipliedBy(ONE_ETH_IN_WEI));
// expect(feeRecipientEthBalanceAfter.minus(feeRecipientEthBalanceBefore)).to.bignumber.equal((new BigNumber(0.5)).multipliedBy(ONE_ETH_IN_WEI));
// });
}); });
}); });
@ -200,17 +212,72 @@ describe('ForwarderSwapQuoteConsumer', () => {
describe('validation', () => { describe('validation', () => {
it('should throw if swap quote provided is not a valid forwarder SwapQuote (taker asset is WETH)', async () => { it('should throw if swap quote provided is not a valid forwarder SwapQuote (taker asset is WETH)', async () => {
const invalidSignedOrders = await getSignedOrdersWithNoFeesAsync(
provider,
makerAssetData,
takerAssetData,
makerAddress,
takerAddress,
FILLABLE_AMOUNTS,
);
const invalidSwapQuote = getFullyFillableSwapQuoteWithNoFees(
makerAssetData,
takerAssetData,
invalidSignedOrders,
MARKET_OPERATION,
);
expect(
swapQuoteConsumer.getSmartContractParamsOrThrowAsync(invalidSwapQuote, { }),
).to.be.rejectedWith(`Expected quote.orders[0] to have takerAssetData set as ${wethAssetData}, but is ${takerAssetData}`);
}); });
}); });
describe('valid swap quote', async () => { describe('valid swap quote', async () => {
it('provide correct and optimized smart contract params with default options for a marketSell SwapQuote (no affiliate fees)', async () => { it('provide correct and optimized smart contract params with default options for a marketSell SwapQuote (no affiliate fees)', async () => {
const { to, params } = await swapQuoteConsumer.getSmartContractParamsOrThrowAsync(marketSellSwapQuote, {});
expect(to).to.deep.equal(contractWrappers.forwarder.address);
const { feeSignatures, 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);
expect(feeSignatures).to.deep.equal([]);
}); });
it('provide correct and optimized smart contract params with default options for a marketBuy SwapQuote (no affiliate fees)', async () => { it('provide correct and optimized smart contract params with default options for a marketBuy SwapQuote (no affiliate fees)', async () => {
const { to, params } = await swapQuoteConsumer.getSmartContractParamsOrThrowAsync(marketBuySwapQuote, {});
expect(to).to.deep.equal(contractWrappers.forwarder.address);
const { makerAssetFillAmount, feeSignatures, 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);
expect(feeSignatures).to.deep.equal([]);
}); });
it('provide correct and optimized smart contract params with affiliate fees for a marketSell SwapQuote', async () => { it('provide correct and optimized smart contract params with affiliate fees for a marketSell SwapQuote', async () => {
const { to, params } = await swapQuoteConsumer.getSmartContractParamsOrThrowAsync(marketSellSwapQuote, { feePercentage: 0.05, feeRecipient });
expect(to).to.deep.equal(contractWrappers.forwarder.address);
const { feeSignatures, 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));
expect(feeSignatures).to.deep.equal([]);
}); });
it('provide correct and optimized smart contract params with affiliate fees for a marketBuy SwapQuote', async () => { it('provide correct and optimized smart contract params with affiliate fees for a marketBuy SwapQuote', async () => {
const { to, params } = await swapQuoteConsumer.getSmartContractParamsOrThrowAsync(marketBuySwapQuote, { feePercentage: 0.05, feeRecipient });
expect(to).to.deep.equal(contractWrappers.forwarder.address);
const { makerAssetFillAmount, feeSignatures, 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));
expect(feeSignatures).to.deep.equal([]);
}); });
}); });
}); });
@ -218,18 +285,70 @@ describe('ForwarderSwapQuoteConsumer', () => {
describe('getCalldataOrThrow', () => { describe('getCalldataOrThrow', () => {
describe('validation', () => { describe('validation', () => {
it('should throw if swap quote provided is not a valid forwarder SwapQuote (taker asset is WETH)', async () => { it('should throw if swap quote provided is not a valid forwarder SwapQuote (taker asset is WETH)', async () => {
const invalidSignedOrders = await getSignedOrdersWithNoFeesAsync(
provider,
makerAssetData,
takerAssetData,
makerAddress,
takerAddress,
FILLABLE_AMOUNTS,
);
const invalidSwapQuote = getFullyFillableSwapQuoteWithNoFees(
makerAssetData,
takerAssetData,
invalidSignedOrders,
MARKET_OPERATION,
);
expect(
swapQuoteConsumer.getCalldataOrThrowAsync(invalidSwapQuote, { }),
).to.be.rejectedWith(`Expected quote.orders[0] to have takerAssetData set as ${wethAssetData}, but is ${takerAssetData}`);
}); });
}); });
describe('valid swap quote', async () => { describe('valid swap quote', async () => {
it('provide correct and optimized calldata options with default options for a marketSell SwapQuote (no affiliate fees)', async () => { it('provide correct and optimized calldata options with default options for a marketSell SwapQuote (no affiliate fees)', async () => {
let makerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, makerAddress);
let takerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, takerAddress);
expect(makerBalance).to.bignumber.equal((new BigNumber(10)).multipliedBy(ONE_ETH_IN_WEI));
expect(takerBalance).to.bignumber.equal(constants.ZERO_AMOUNT);
const { calldataHexString, to } = await swapQuoteConsumer.getCalldataOrThrowAsync(marketSellSwapQuote, {});
expect(to).to.deep.equal(contractWrappers.forwarder.address);
await web3Wrapper.sendTransactionAsync({
from: takerAddress,
to,
data: calldataHexString,
value: marketSellSwapQuote.worstCaseQuoteInfo.totalTakerTokenAmount,
gas: 4000000,
});
makerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, makerAddress);
takerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, takerAddress);
expect(makerBalance).to.bignumber.equal((new BigNumber(0.5).multipliedBy(ONE_ETH_IN_WEI)));
expect(takerBalance).to.bignumber.equal((new BigNumber(9.5)).multipliedBy(ONE_ETH_IN_WEI));
}); });
it('provide correct and optimized calldata options with default options for a marketBuy SwapQuote (no affiliate fees)', async () => { it('provide correct and optimized calldata options with default options for a marketBuy SwapQuote (no affiliate fees)', async () => {
let makerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, makerAddress);
let takerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, takerAddress);
expect(makerBalance).to.bignumber.equal((new BigNumber(10)).multipliedBy(ONE_ETH_IN_WEI));
expect(takerBalance).to.bignumber.equal(constants.ZERO_AMOUNT);
const { calldataHexString, to } = await swapQuoteConsumer.getCalldataOrThrowAsync(marketBuySwapQuote, {});
expect(to).to.deep.equal(contractAddresses.forwarder);
await web3Wrapper.sendTransactionAsync({
from: takerAddress,
to,
data: calldataHexString,
value: marketBuySwapQuote.worstCaseQuoteInfo.totalTakerTokenAmount,
gas: 4000000,
}); });
it('provide correct and optimized calldata options with affiliate fees for a marketSell SwapQuote', async () => { makerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, makerAddress);
}); takerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, takerAddress);
it('provide correct and optimized calldata options with affiliate fees for a marketBuy SwapQuote', async () => { expect(takerBalance).to.bignumber.equal((new BigNumber(10)).multipliedBy(ONE_ETH_IN_WEI));
expect(makerBalance).to.bignumber.equal(constants.ZERO_AMOUNT);
}); });
// TODO(david) finish testing for affiliate fees calldata output
// it('provide correct and optimized calldata options with affiliate fees for a marketSell SwapQuote', async () => {
// });
// it('provide correct and optimized calldata options with affiliate fees for a marketBuy SwapQuote', async () => {
// });
}); });
}); });
}); });

View File

@ -1,3 +1,4 @@
import { ContractAddresses, ContractWrappers } from '@0x/contract-wrappers';
import { tokenUtils } from '@0x/contract-wrappers/lib/test/utils/token_utils'; import { tokenUtils } from '@0x/contract-wrappers/lib/test/utils/token_utils';
import { BlockchainLifecycle } from '@0x/dev-utils'; import { BlockchainLifecycle } from '@0x/dev-utils';
import { FillScenarios } from '@0x/fill-scenarios'; import { FillScenarios } from '@0x/fill-scenarios';
@ -7,14 +8,14 @@ import { BigNumber } from '@0x/utils';
import * as chai from 'chai'; import * as chai from 'chai';
import 'mocha'; import 'mocha';
import { SwapQuote, SwapQuoteConsumer } from '../src';
import { ConsumerType } from '../src/types';
import { swapQuoteConsumerUtils } from '../src/utils/swap_quote_consumer_utils';
import { chaiSetup } from './utils/chai_setup'; import { chaiSetup } from './utils/chai_setup';
import { migrateOnceAsync } from './utils/migrate'; import { migrateOnceAsync } from './utils/migrate';
import { getFullyFillableSwapQuoteWithNoFees, getSignedOrdersWithNoFeesAsync } from './utils/swap_quote'; import { getFullyFillableSwapQuoteWithNoFees, getSignedOrdersWithNoFeesAsync } from './utils/swap_quote';
import { provider, web3Wrapper } from './utils/web3_wrapper'; import { provider, web3Wrapper } from './utils/web3_wrapper';
import { SwapQuote, SwapQuoteConsumer } from '../src';
import { ContractWrappers, ContractAddresses } from '@0x/contract-wrappers';
import { swapQuoteConsumerUtils } from '../src/utils/swap_quote_consumer_utils';
import { ConsumerType } from '../src/types';
chaiSetup.configure(); chaiSetup.configure();
const expect = chai.expect; const expect = chai.expect;