From c8d0ff846cdbfac94aacee217c8042f8fc5d7ff7 Mon Sep 17 00:00:00 2001 From: David Sun Date: Thu, 25 Jul 2019 14:07:32 -0700 Subject: [PATCH] Finished up adding test coverage --- .../exchange_swap_quote_consumer.ts | 2 +- .../forwarder_swap_quote_consumer.ts | 3 +- packages/asset-swapper/src/utils/assert.ts | 4 +- .../src/utils/swap_quote_consumer_utils.ts | 2 +- .../test/exchange_swap_quote_consumer_test.ts | 137 +++++--- .../forwarder_swap_quote_consumer_test.ts | 295 ++++++++++++------ .../test/swap_quote_consumer_utils_test.ts | 9 +- 7 files changed, 304 insertions(+), 148 deletions(-) diff --git a/packages/asset-swapper/src/quote_consumers/exchange_swap_quote_consumer.ts b/packages/asset-swapper/src/quote_consumers/exchange_swap_quote_consumer.ts index edbcffe0d9..0b4cb189ed 100644 --- a/packages/asset-swapper/src/quote_consumers/exchange_swap_quote_consumer.ts +++ b/packages/asset-swapper/src/quote_consumers/exchange_swap_quote_consumer.ts @@ -58,7 +58,7 @@ export class ExchangeSwapQuoteConsumer implements SwapQuoteConsumerBase { assert.assert( order.takerAssetData === takerAssetData, - `Expected ${variableName}[${index}].takerAssetData to be ${takerAssetData}`, + `Expected ${variableName}[${index}].takerAssetData to be ${takerAssetData} but found ${order.takerAssetData}`, ); assert.assert( order.makerAssetData === makerAssetData, - `Expected ${variableName}[${index}].makerAssetData to be ${makerAssetData}`, + `Expected ${variableName}[${index}].makerAssetData to be ${makerAssetData} but found ${order.makerAssetData}`, ); }); }, diff --git a/packages/asset-swapper/src/utils/swap_quote_consumer_utils.ts b/packages/asset-swapper/src/utils/swap_quote_consumer_utils.ts index 443b49b290..861eb5c419 100644 --- a/packages/asset-swapper/src/utils/swap_quote_consumer_utils.ts +++ b/packages/asset-swapper/src/utils/swap_quote_consumer_utils.ts @@ -84,7 +84,7 @@ export const swapQuoteConsumerUtils = { return optimizedOrder; }); }, - async getConsumerForSwapQuoteAsync( + async getConsumerTypeForSwapQuoteAsync( quote: SwapQuote, contractWrappers: ContractWrappers, provider: Provider, diff --git a/packages/asset-swapper/test/exchange_swap_quote_consumer_test.ts b/packages/asset-swapper/test/exchange_swap_quote_consumer_test.ts index 51ec6af37e..269d041d45 100644 --- a/packages/asset-swapper/test/exchange_swap_quote_consumer_test.ts +++ b/packages/asset-swapper/test/exchange_swap_quote_consumer_test.ts @@ -8,10 +8,11 @@ import { BigNumber } from '@0x/utils'; import * as chai from 'chai'; 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 { 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 { migrateOnceAsync } from './utils/migrate'; @@ -44,6 +45,12 @@ describe('ExchangeSwapQuoteConsumer', () => { let contractAddresses: ContractAddresses; const networkId = TESTRPC_NETWORK_ID; + + let orders: SignedOrder[]; + let marketSellSwapQuote: SwapQuote; + let marketBuySwapQuote: SwapQuote; + let swapQuoteConsumer: ExchangeSwapQuoteConsumer; + before(async () => { contractAddresses = await migrateOnceAsync(); await blockchainLifecycle.startAsync(); @@ -74,48 +81,41 @@ describe('ExchangeSwapQuoteConsumer', () => { }); beforeEach(async () => { await blockchainLifecycle.startAsync(); + orders = []; + for (const fillableAmmount of FILLABLE_AMOUNTS) { + const order = await fillScenarios.createFillableSignedOrderAsync( + makerAssetData, + takerAssetData, + makerAddress, + takerAddress, + fillableAmmount, + ); + orders.push(order); + } + + marketSellSwapQuote = getFullyFillableSwapQuoteWithNoFees( + makerAssetData, + takerAssetData, + orders, + MarketOperation.Sell, + ); + + marketBuySwapQuote = getFullyFillableSwapQuoteWithNoFees( + makerAssetData, + takerAssetData, + orders, + MarketOperation.Buy, + ); + + swapQuoteConsumer = new ExchangeSwapQuoteConsumer(provider, { + networkId, + }); }); 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) { - const order = await fillScenarios.createFillableSignedOrderAsync( - makerAssetData, - takerAssetData, - makerAddress, - takerAddress, - fillableAmmount, - ); - orders.push(order); - } - - marketSellSwapQuote = getFullyFillableSwapQuoteWithNoFees( - makerAssetData, - wethAssetData, - orders, - MarketOperation.Sell, - ); - - marketBuySwapQuote = getFullyFillableSwapQuoteWithNoFees( - makerAssetData, - wethAssetData, - orders, - MarketOperation.Buy, - ); - - swapQuoteConsumer = new ExchangeSwapQuoteConsumer(provider, { - networkId, - }); - }); /* * 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 @@ -143,32 +143,69 @@ describe('ExchangeSwapQuoteConsumer', () => { expect(makerBalance).to.bignumber.equal(constants.ZERO_AMOUNT); }); }); - }); describe('getSmartContractParamsOrThrow', () => { 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('validation', () => { - it('should throw if swap quote provided is not a valid forwarder SwapQuote (taker asset is WETH)', async () => { - }); - }); - describe('valid swap quote', 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 affiliate fees for a marketSell SwapQuote', async () => { - }); - it('provide correct and optimized calldata options with affiliate fees for a marketBuy SwapQuote', 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, + }); + 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); }); }); }); diff --git a/packages/asset-swapper/test/forwarder_swap_quote_consumer_test.ts b/packages/asset-swapper/test/forwarder_swap_quote_consumer_test.ts index f55983619d..1b5b6d9137 100644 --- a/packages/asset-swapper/test/forwarder_swap_quote_consumer_test.ts +++ b/packages/asset-swapper/test/forwarder_swap_quote_consumer_test.ts @@ -4,7 +4,7 @@ import { BlockchainLifecycle } from '@0x/dev-utils'; import { FillScenarios } from '@0x/fill-scenarios'; import { assetDataUtils } from '@0x/order-utils'; import { MarketOperation, SignedOrder } from '@0x/types'; -import { BigNumber } from '@0x/utils'; +import { BigNumber, AbiDecoder } from '@0x/utils'; import * as chai from 'chai'; import 'mocha'; @@ -15,6 +15,8 @@ import { chaiSetup } from './utils/chai_setup'; import { migrateOnceAsync } from './utils/migrate'; import { getFullyFillableSwapQuoteWithNoFees, getSignedOrdersWithNoFeesAsync } from './utils/swap_quote'; import { provider, web3Wrapper } from './utils/web3_wrapper'; +import { constants } from '../src/constants'; +import { ForwarderMarketSellSmartContractParams, MarketSellSwapQuote, ForwarderMarketBuySmartContractParams, MarketBuySwapQuote } from '../src/types'; chaiSetup.configure(); const expect = chai.expect; @@ -41,6 +43,12 @@ describe('ForwarderSwapQuoteConsumer', () => { let wethAssetData: string; let contractAddresses: ContractAddresses; + let orders: SignedOrder[]; + let marketSellSwapQuote: SwapQuote; + let marketBuySwapQuote: SwapQuote; + let swapQuoteConsumer: ForwarderSwapQuoteConsumer; + let erc20ProxyAddress: string; + const networkId = TESTRPC_NETWORK_ID; before(async () => { contractAddresses = await migrateOnceAsync(); @@ -72,6 +80,41 @@ describe('ForwarderSwapQuoteConsumer', () => { }); beforeEach(async () => { await blockchainLifecycle.startAsync(); + const UNLIMITED_ALLOWANCE = contractWrappers.erc20Token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; + erc20ProxyAddress = contractWrappers.erc20Proxy.address; + + const totalFillableAmount = FILLABLE_AMOUNTS.reduce((a: BigNumber, c: BigNumber) => a.plus(c), new BigNumber(0)); + + await contractWrappers.erc20Token.transferAsync(makerTokenAddress, coinbaseAddress, makerAddress, totalFillableAmount); + + await contractWrappers.erc20Token.setAllowanceAsync(makerTokenAddress, makerAddress, erc20ProxyAddress, UNLIMITED_ALLOWANCE); + orders = await getSignedOrdersWithNoFeesAsync( + provider, + makerAssetData, + wethAssetData, + makerAddress, + takerAddress, + FILLABLE_AMOUNTS, + contractAddresses.exchange, + ); + + marketSellSwapQuote = getFullyFillableSwapQuoteWithNoFees( + makerAssetData, + wethAssetData, + orders, + MarketOperation.Sell, + ); + + marketBuySwapQuote = getFullyFillableSwapQuoteWithNoFees( + makerAssetData, + wethAssetData, + orders, + MarketOperation.Buy, + ); + + swapQuoteConsumer = new ForwarderSwapQuoteConsumer(provider, { + networkId, + }); }); afterEach(async () => { await blockchainLifecycle.revertAsync(); @@ -94,105 +137,74 @@ describe('ForwarderSwapQuoteConsumer', () => { 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}`); }); }); + // TODO(david) test execution of swap quotes with fee orders 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; - erc20ProxyAddress = contractWrappers.erc20Proxy.address; - - const totalFillableAmount = FILLABLE_AMOUNTS.reduce((a: BigNumber, c: BigNumber) => a.plus(c), new BigNumber(0)); - - await contractWrappers.erc20Token.transferAsync(makerTokenAddress, coinbaseAddress, makerAddress, totalFillableAmount); - - await contractWrappers.erc20Token.setAllowanceAsync(makerTokenAddress, makerAddress, erc20ProxyAddress, UNLIMITED_ALLOWANCE); - orders = await getSignedOrdersWithNoFeesAsync( - provider, - makerAssetData, - wethAssetData, - makerAddress, - takerAddress, - FILLABLE_AMOUNTS, - contractAddresses.exchange, - ); - - marketSellSwapQuote = getFullyFillableSwapQuoteWithNoFees( - makerAssetData, - wethAssetData, - orders, - MarketOperation.Sell, - ); - - marketBuySwapQuote = getFullyFillableSwapQuoteWithNoFees( - makerAssetData, - wethAssetData, - orders, - MarketOperation.Buy, - ); - - swapQuoteConsumer = new ForwarderSwapQuoteConsumer(provider, { - networkId, - }); - }); /* * 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 marketSell execution when provided a MarketSell 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); - // const txHash = await swapQuoteConsumer.executeSwapQuoteOrThrowAsync(marketSellSwapQuote, { takerAddress }); - // console.log(txHash); - // // expect( - // // 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); + 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); + await swapQuoteConsumer.executeSwapQuoteOrThrowAsync(marketSellSwapQuote, { takerAddress }); + 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('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); + 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); + 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 marketSell execution with affiliate fees', () => {}); - it('should perform a marketSell execution with provided ethAmount in options', () => {}); - it('should throw on a marketSell execution with provided ethAmount that is lower than bestCaseQuoteInfo.totalTakerAssetAmount in options', () => {}); - it('should perform a marketSell execution with provided takerAddress in options', () => {}); + 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', () => { 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 () => { 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 () => { + 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 () => { + 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 () => { + 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('validation', () => { 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 () => { 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 () => { + 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, + }); + 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 affiliate fees for a marketSell SwapQuote', async () => { - }); - it('provide correct and optimized calldata options with affiliate fees for a marketBuy SwapQuote', async () => { - }); + // 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 () => { + // }); }); }); }); diff --git a/packages/asset-swapper/test/swap_quote_consumer_utils_test.ts b/packages/asset-swapper/test/swap_quote_consumer_utils_test.ts index f86e99de87..0ddfeb7f74 100644 --- a/packages/asset-swapper/test/swap_quote_consumer_utils_test.ts +++ b/packages/asset-swapper/test/swap_quote_consumer_utils_test.ts @@ -1,3 +1,4 @@ +import { ContractAddresses, ContractWrappers } from '@0x/contract-wrappers'; import { tokenUtils } from '@0x/contract-wrappers/lib/test/utils/token_utils'; import { BlockchainLifecycle } from '@0x/dev-utils'; import { FillScenarios } from '@0x/fill-scenarios'; @@ -7,14 +8,14 @@ import { BigNumber } from '@0x/utils'; import * as chai from 'chai'; 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 { migrateOnceAsync } from './utils/migrate'; import { getFullyFillableSwapQuoteWithNoFees, getSignedOrdersWithNoFeesAsync } from './utils/swap_quote'; 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(); const expect = chai.expect;