diff options
| author | Amir Bandeali <abandeali1@gmail.com> | 2018-07-18 02:33:51 +0800 | 
|---|---|---|
| committer | Amir Bandeali <abandeali1@gmail.com> | 2018-07-23 23:00:23 +0800 | 
| commit | 1f0e819756ea247e592a8a05c53561894c8ca87a (patch) | |
| tree | 7dc3a02a83f35a17dbdd1558238b564d97470e8d /packages | |
| parent | 799ff2a5c392383c8b245ae53057593acc2534ce (diff) | |
| download | dexon-sol-tools-1f0e819756ea247e592a8a05c53561894c8ca87a.tar.gz dexon-sol-tools-1f0e819756ea247e592a8a05c53561894c8ca87a.tar.zst dexon-sol-tools-1f0e819756ea247e592a8a05c53561894c8ca87a.zip | |
Fix minimal tests
Diffstat (limited to 'packages')
| -rw-r--r-- | packages/contracts/test/forwarder/forwarder.ts | 1248 | ||||
| -rw-r--r-- | packages/contracts/test/utils/constants.ts | 2 | ||||
| -rw-r--r-- | packages/contracts/test/utils/forwarder_wrapper.ts | 226 | ||||
| -rw-r--r-- | packages/types/src/index.ts | 2 | 
4 files changed, 748 insertions, 730 deletions
| diff --git a/packages/contracts/test/forwarder/forwarder.ts b/packages/contracts/test/forwarder/forwarder.ts index 0256d7d81..f2966fe75 100644 --- a/packages/contracts/test/forwarder/forwarder.ts +++ b/packages/contracts/test/forwarder/forwarder.ts @@ -47,10 +47,10 @@ describe(ContractName.Forwarder, () => {      let forwarderWrapper: ForwarderWrapper;      let exchangeWrapper: ExchangeWrapper; -    let signedOrder: SignedOrder; -    let signedOrders: SignedOrder[]; +    let orderWithoutFee: SignedOrder; +    let ordersWithoutFee: SignedOrder[];      let orderWithFee: SignedOrder; -    let signedOrdersWithFee: SignedOrder[]; +    let ordersWithFee: SignedOrder[];      let feeOrder: SignedOrder;      let feeOrders: SignedOrder[];      let orderFactory: OrderFactory; @@ -59,7 +59,9 @@ describe(ContractName.Forwarder, () => {      let tx: TransactionReceiptWithDecodedLogs;      let erc721MakerAssetIds: BigNumber[]; -    let feeProportion: number = 0; +    let takerEthBalanceBefore: BigNumber; +    let feePercentage: BigNumber; +    const MAX_WETH_FILL_PERCENTAGE = 95;      before(async () => {          await blockchainLifecycle.startAsync(); @@ -135,7 +137,11 @@ describe(ContractName.Forwarder, () => {              wethAssetData,          );          forwarderContract = new ForwarderContract(forwarderInstance.abi, forwarderInstance.address, provider); -        forwarderWrapper = new ForwarderWrapper(forwarderContract, provider, zrxToken.address); +        forwarderWrapper = new ForwarderWrapper(forwarderContract, provider); +        const zrxDepositAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(10000), 18); +        await web3Wrapper.awaitTransactionSuccessAsync( +            await zrxToken.transfer.sendTransactionAsync(forwarderContract.address, zrxDepositAmount), +        );          erc20Wrapper.addTokenOwnerAddress(forwarderInstance.address);          web3Wrapper.abiDecoder.addABI(forwarderContract.abi); @@ -146,673 +152,785 @@ describe(ContractName.Forwarder, () => {      });      beforeEach(async () => {          await blockchainLifecycle.startAsync(); -        feeProportion = 0;          erc20Balances = await erc20Wrapper.getBalancesAsync(); -        signedOrder = await orderFactory.newSignedOrderAsync(); -        signedOrders = [signedOrder]; +        takerEthBalanceBefore = await web3Wrapper.getBalanceInWeiAsync(takerAddress); +        orderWithoutFee = await orderFactory.newSignedOrderAsync();          feeOrder = await orderFactory.newSignedOrderAsync({              makerAssetData: assetDataUtils.encodeERC20AssetData(zrxToken.address),              takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT),          }); -        feeOrders = [feeOrder];          orderWithFee = await orderFactory.newSignedOrderAsync({              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', () => { + +    describe('marketSellOrdersWithEth 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]; +            ordersWithoutFee = [orderWithoutFee];              feeOrders = []; -            tx = await forwarderWrapper.marketSellEthForERC20Async(signedOrders, feeOrders, { -                value: fillAmount, +            const ethValue = orderWithoutFee.takerAssetAmount.dividedToIntegerBy(2); + +            tx = await forwarderWrapper.marketSellOrdersWithEthAsync(ordersWithoutFee, feeOrders, { +                value: ethValue,                  from: takerAddress,              }); +            const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress); +            const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);              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)); + +            const primaryTakerAssetFillAmount = ForwarderWrapper.getPercentageOfValue( +                ethValue, +                MAX_WETH_FILL_PERCENTAGE, +            ); +            const makerAssetFillAmount = primaryTakerAssetFillAmount +                .times(orderWithoutFee.makerAssetAmount) +                .dividedToIntegerBy(orderWithoutFee.takerAssetAmount); +            const totalEthSpent = primaryTakerAssetFillAmount.plus(DEFAULT_GAS_PRICE.times(tx.gasUsed)); + +            expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent)); +            expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( +                erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), +            ); +            expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( +                erc20Balances[takerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount), +            ); +            expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal( +                erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount), +            ); +            expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT); +            expect(newBalances[forwarderContract.address][defaultMakerAssetAddress]).to.be.bignumber.equal( +                constants.ZERO_AMOUNT, +            ); +            expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);          }); -        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, +        it('should fill the order and pay ZRX fees from feeOrders', async () => { +            ordersWithFee = [orderWithFee]; +            feeOrders = [feeOrder]; +            const ethValue = orderWithFee.takerAssetAmount.dividedToIntegerBy(2); + +            tx = await forwarderWrapper.marketSellOrdersWithEthAsync(ordersWithFee, feeOrders, { +                value: ethValue,                  from: takerAddress,              }); +            const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress); +            const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);              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)); +            const primaryTakerAssetFillAmount = ForwarderWrapper.getPercentageOfValue( +                ethValue, +                MAX_WETH_FILL_PERCENTAGE, +            ); +            const makerAssetFillAmount = primaryTakerAssetFillAmount +                .times(orderWithoutFee.makerAssetAmount) +                .dividedToIntegerBy(orderWithoutFee.takerAssetAmount); +            const feeAmount = ForwarderWrapper.getPercentageOfValue( +                orderWithFee.takerFee.dividedToIntegerBy(2), +                MAX_WETH_FILL_PERCENTAGE, +            ); +            const wethSpentOnFeeOrders = ForwarderWrapper.getWethForFeeOrders(feeAmount, feeOrders); +            const totalEthSpent = primaryTakerAssetFillAmount +                .plus(wethSpentOnFeeOrders) +                .plus(DEFAULT_GAS_PRICE.times(tx.gasUsed)); + +            expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent)); +            expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( +                erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), +            ); +            expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( +                erc20Balances[takerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount), +            ); +            expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal( +                erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount).plus(wethSpentOnFeeOrders), +            ); +            expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT); +            expect(newBalances[forwarderContract.address][defaultMakerAssetAddress]).to.be.bignumber.equal( +                constants.ZERO_AMOUNT, +            ); +            expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);          });          it('should fill the order when token is ZRX with fees', async () => {              orderWithFee = await orderFactory.newSignedOrderAsync({                  makerAssetData: assetDataUtils.encodeERC20AssetData(zrxToken.address),                  takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT),              }); -            signedOrdersWithFee = [orderWithFee]; +            ordersWithFee = [orderWithFee];              feeOrders = []; -            const fillAmount = signedOrder.takerAssetAmount.div(4); -            const takerBalanceBefore = erc20Balances[takerAddress][zrxToken.address]; -            tx = await forwarderWrapper.marketSellEthForERC20Async(signedOrdersWithFee, feeOrders, { -                value: fillAmount, +            const ethValue = orderWithFee.takerAssetAmount.dividedToIntegerBy(2); + +            tx = await forwarderWrapper.marketSellOrdersWithEthAsync(ordersWithFee, feeOrders, { +                value: ethValue,                  from: takerAddress,              }); +            const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress); +            const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);              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)); +            const makerAssetFillAmount = orderWithFee.makerAssetAmount.dividedToIntegerBy(2); +            const totalEthSpent = ethValue.plus(DEFAULT_GAS_PRICE.times(tx.gasUsed)); +            const takerFeePaid = orderWithFee.takerFee.dividedToIntegerBy(2); +            const makerFeePaid = orderWithFee.makerFee.dividedToIntegerBy(2); + +            expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent)); +            expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal( +                erc20Balances[makerAddress][zrxToken.address].minus(makerAssetFillAmount).minus(makerFeePaid), +            ); +            expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal( +                erc20Balances[takerAddress][zrxToken.address].plus(makerAssetFillAmount).minus(takerFeePaid), +            ); +            expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal( +                erc20Balances[makerAddress][weth.address].plus(ethValue), +            ); +            expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT); +            expect(newBalances[forwarderContract.address][zrxToken.address]).to.be.bignumber.equal( +                erc20Balances[forwarderContract.address][zrxToken.address], +            ); +            expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);          }); -        it('should fail if sent an ETH amount too high', async () => { -            signedOrder = await orderFactory.newSignedOrderAsync({ -                makerAssetData: assetDataUtils.encodeERC20AssetData(zrxToken.address), -                takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT), +        it('should refund remaining ETH if amount is greater than takerAssetAmount', async () => { +            ordersWithoutFee = [orderWithoutFee]; +            const ethValue = orderWithoutFee.takerAssetAmount.times(2); + +            tx = await forwarderWrapper.marketSellOrdersWithEthAsync(ordersWithoutFee, feeOrders, { +                value: ethValue, +                from: takerAddress,              }); -            const fillAmount = signedOrder.takerAssetAmount.times(2); -            return expectTransactionFailedAsync( -                forwarderWrapper.marketSellEthForERC20Async(signedOrdersWithFee, feeOrders, { -                    value: fillAmount, -                    from: takerAddress, -                }), -                RevertReason.UnacceptableThreshold, -            ); +            const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress); +            const totalEthSpent = orderWithoutFee.takerAssetAmount.plus(DEFAULT_GAS_PRICE.times(tx.gasUsed)); + +            expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));          }); -        it('should fail if fee abstraction amount is too high', async () => { +        it('should revert if ZRX cannot be fully repurchased', async () => {              orderWithFee = await orderFactory.newSignedOrderAsync({                  takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), DECIMALS_DEFAULT),              }); -            signedOrdersWithFee = [orderWithFee]; +            ordersWithFee = [orderWithFee];              feeOrder = await orderFactory.newSignedOrderAsync({                  makerAssetData: assetDataUtils.encodeERC20AssetData(zrxToken.address),                  makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT),              });              feeOrders = [feeOrder]; -            const fillAmount = signedOrder.takerAssetAmount.div(4); +            const ethValue = orderWithFee.takerAssetAmount;              return expectTransactionFailedAsync( -                forwarderWrapper.marketSellEthForERC20Async(signedOrdersWithFee, feeOrders, { -                    value: fillAmount, +                forwarderWrapper.marketSellOrdersWithEthAsync(ordersWithFee, feeOrders, { +                    value: ethValue,                      from: takerAddress,                  }), -                RevertReason.TransferFailed, +                RevertReason.CompleteFillFailed,              );          }); -        it('throws when mixed ERC721 and ERC20 assets with ERC20 first', async () => { +        it('should not fill orders with different makerAssetData than the first order', async () => {              const makerAssetId = erc721MakerAssetIds[0];              const erc721SignedOrder = await orderFactory.newSignedOrderAsync({                  makerAssetAmount: new BigNumber(1),                  makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),              });              const erc20SignedOrder = await orderFactory.newSignedOrderAsync(); -            signedOrders = [erc20SignedOrder, erc721SignedOrder]; -            const fillAmountWei = erc20SignedOrder.takerAssetAmount.plus(erc721SignedOrder.takerAssetAmount); -            return expectTransactionFailedAsync( -                forwarderWrapper.marketSellEthForERC20Async(signedOrders, feeOrders, { -                    from: takerAddress, -                    value: fillAmountWei, -                }), -                RevertReason.InvalidOrderSignature, -            ); +            ordersWithoutFee = [erc20SignedOrder, erc721SignedOrder]; +            const ethValue = erc20SignedOrder.takerAssetAmount.plus(erc721SignedOrder.takerAssetAmount); + +            tx = await forwarderWrapper.marketSellOrdersWithEthAsync(ordersWithoutFee, feeOrders, { +                value: ethValue, +                from: takerAddress, +            }); +            const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress); +            const totalEthSpent = erc20SignedOrder.takerAssetAmount.plus(DEFAULT_GAS_PRICE.times(tx.gasUsed)); + +            expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));          });      }); -    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% +    describe('marketSellOrdersWithEth with extra fees', () => { +        it('should fill the order and send fee to feeRecipient', async () => { +            ordersWithoutFee = [orderWithoutFee];              feeOrders = []; -            tx = await forwarderWrapper.marketSellEthForERC20Async( -                signedOrders, +            const ethValue = orderWithoutFee.takerAssetAmount.div(2); + +            const baseFeePercentage = 2; +            feePercentage = ForwarderWrapper.getPercentageOfValue(constants.PERCENTAGE_DENOMINATOR, baseFeePercentage); +            const feeRecipientEthBalanceBefore = await web3Wrapper.getBalanceInWeiAsync(feeRecipientAddress); +            tx = await forwarderWrapper.marketSellOrdersWithEthAsync( +                ordersWithoutFee,                  feeOrders,                  { +                    value: ethValue,                      from: takerAddress, -                    value: fillAmount, -                    gasPrice: DEFAULT_GAS_PRICE, -                }, -                { -                    feeProportion, -                    feeRecipient: feeRecipientAddress,                  }, +                { feePercentage, feeRecipient: feeRecipientAddress },              ); +            const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress); +            const feeRecipientEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(feeRecipientAddress); +            const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);              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)), +            const primaryTakerAssetFillAmount = ForwarderWrapper.getPercentageOfValue( +                ethValue, +                MAX_WETH_FILL_PERCENTAGE, +            ); +            const makerAssetFillAmount = primaryTakerAssetFillAmount +                .times(orderWithoutFee.makerAssetAmount) +                .dividedToIntegerBy(orderWithoutFee.takerAssetAmount); +            const ethSpentOnFee = ForwarderWrapper.getPercentageOfValue(primaryTakerAssetFillAmount, baseFeePercentage); +            const totalEthSpent = primaryTakerAssetFillAmount +                .plus(ethSpentOnFee) +                .plus(DEFAULT_GAS_PRICE.times(tx.gasUsed)); + +            expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent)); +            expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( +                erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount),              ); -            expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(new BigNumber(0)); +            expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( +                erc20Balances[takerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount), +            ); +            expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal( +                erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount), +            ); +            expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT); +            expect(newBalances[forwarderContract.address][defaultMakerAssetAddress]).to.be.bignumber.equal( +                constants.ZERO_AMOUNT, +            ); +            expect(feeRecipientEthBalanceAfter).to.be.bignumber.equal(feeRecipientEthBalanceBefore.plus(ethSpentOnFee)); +            expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);          });          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% +            const feeRecipientEthBalanceBefore = await web3Wrapper.getBalanceInWeiAsync(feeRecipientAddress); +            const ethValue = orderWithoutFee.takerAssetAmount.div(2); +            const baseFeePercentage = 6; +            feePercentage = ForwarderWrapper.getPercentageOfValue(ethValue, baseFeePercentage);              feeOrders = [];              await expectTransactionFailedAsync( -                forwarderWrapper.marketSellEthForERC20Async( -                    signedOrders, +                forwarderWrapper.marketSellOrdersWithEthAsync( +                    ordersWithoutFee,                      feeOrders, -                    { from: takerAddress, value: fillAmount, gasPrice: DEFAULT_GAS_PRICE }, -                    { feeProportion, feeRecipient: feeRecipientAddress }, +                    { from: takerAddress, value: ethValue, gasPrice: DEFAULT_GAS_PRICE }, +                    { feePercentage, feeRecipient: feeRecipientAddress },                  ), -                RevertReason.FeeProportionTooLarge, +                RevertReason.FeePercentageTooLarge,              ); -            const afterEthBalance = await web3Wrapper.getBalanceInWeiAsync(feeRecipientAddress); -            expect(afterEthBalance).to.be.bignumber.equal(initEthBalance); +            const feeRecipientEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(feeRecipientAddress); +            expect(feeRecipientEthBalanceAfter).to.be.bignumber.equal(feeRecipientEthBalanceBefore);          });      }); -    describe('marketBuyTokensWithEth', () => { +    describe('marketBuyOrdersWithEth without extra fees', () => {          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); +            ordersWithoutFee = [orderWithoutFee];              feeOrders = []; -            tx = await forwarderWrapper.marketBuyTokensWithEthAsync(signedOrders, feeOrders, makerAssetAmount, { +            const makerAssetFillAmount = orderWithoutFee.makerAssetAmount.dividedToIntegerBy(2); +            const ethValue = orderWithoutFee.takerAssetAmount.dividedToIntegerBy(2); + +            tx = await forwarderWrapper.marketBuyOrdersWithEthAsync(ordersWithoutFee, feeOrders, makerAssetFillAmount, { +                value: ethValue,                  from: takerAddress, -                value: fillAmountWei, -                gasPrice: DEFAULT_GAS_PRICE,              }); +            const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress); +            const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);              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); + +            const primaryTakerAssetFillAmount = ethValue; +            const totalEthSpent = primaryTakerAssetFillAmount.plus(DEFAULT_GAS_PRICE.times(tx.gasUsed)); + +            expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent)); +            expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( +                erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), +            ); +            expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( +                erc20Balances[takerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount), +            ); +            expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal( +                erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount), +            ); +            expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT); +            expect(newBalances[forwarderContract.address][defaultMakerAssetAddress]).to.be.bignumber.equal( +                constants.ZERO_AMOUNT, +            ); +            expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);          });          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 = await orderFactory.newSignedOrderAsync({ -                makerAssetData: assetDataUtils.encodeERC20AssetData(zrxToken.address), -                takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT), -            }); -            signedOrdersWithFee = [signedOrder]; +            ordersWithoutFee = [orderWithoutFee];              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, { +            const makerAssetFillAmount = orderWithoutFee.makerAssetAmount.dividedToIntegerBy(2); +            const ethValue = orderWithoutFee.takerAssetAmount; + +            tx = await forwarderWrapper.marketBuyOrdersWithEthAsync(ordersWithoutFee, feeOrders, makerAssetFillAmount, { +                value: ethValue,                  from: takerAddress, -                value: fillAmountWei, -                gasPrice: DEFAULT_GAS_PRICE,              }); +            const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress); +            const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);              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 = await orderFactory.newSignedOrderAsync({ -                makerAssetData: assetDataUtils.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, + +            const primaryTakerAssetFillAmount = ethValue.dividedToIntegerBy(2); +            const totalEthSpent = primaryTakerAssetFillAmount.plus(DEFAULT_GAS_PRICE.times(tx.gasUsed)); + +            expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent)); +            expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( +                erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount),              ); -            return expectTransactionFailedAsync( -                forwarderWrapper.marketBuyTokensWithEthAsync(signedOrdersWithFee, feeOrders, makerAssetAmount, { -                    from: takerAddress, -                    value: fillAmountWei, -                }), -                RevertReason.UnacceptableThreshold, +            expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( +                erc20Balances[takerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount),              ); -        }); -        it('throws if fees are higher than 5% when buying erc20', async () => { -            const highFeeERC20Order = await orderFactory.newSignedOrderAsync({ -                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, +            expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal( +                erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount),              ); -            return expectTransactionFailedAsync( -                forwarderWrapper.marketBuyTokensWithEthAsync(signedOrdersWithFee, feeOrders, makerAssetAmount, { -                    from: takerAddress, -                    value: fillAmountWei, -                }), -                RevertReason.UnacceptableThreshold as any, +            expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT); +            expect(newBalances[forwarderContract.address][defaultMakerAssetAddress]).to.be.bignumber.equal( +                constants.ZERO_AMOUNT,              ); +            expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);          }); -        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({ +        it('should buy the exact amount of assets with fee abstraction', async () => { +            ordersWithFee = [orderWithFee]; +            feeOrders = [feeOrder]; +            const makerAssetFillAmount = orderWithFee.makerAssetAmount.dividedToIntegerBy(2); +            const ethValue = orderWithoutFee.takerAssetAmount; + +            tx = await forwarderWrapper.marketBuyOrdersWithEthAsync(ordersWithFee, feeOrders, makerAssetFillAmount, { +                value: ethValue,                  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, +            const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress); +            const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address); +            const newBalances = await erc20Wrapper.getBalancesAsync(); + +            const primaryTakerAssetFillAmount = orderWithFee.takerAssetAmount.dividedToIntegerBy(2); +            const feeAmount = orderWithFee.takerFee.dividedToIntegerBy(2); +            const wethSpentOnFeeOrders = ForwarderWrapper.getWethForFeeOrders(feeAmount, feeOrders); +            const totalEthSpent = primaryTakerAssetFillAmount +                .plus(wethSpentOnFeeOrders) +                .plus(DEFAULT_GAS_PRICE.times(tx.gasUsed)); + +            expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent)); +            expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( +                erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount),              ); -        }); -    }); -    describe('marketBuyTokensWithEth - ERC721', async () => { -        it('buys ERC721 assets', async () => { -            const makerAssetId = erc721MakerAssetIds[0]; -            signedOrder = await orderFactory.newSignedOrderAsync({ -                makerAssetAmount: new BigNumber(1), -                makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId), -            }); -            feeOrders = []; -            signedOrders = [signedOrder]; -            const makerAssetAmount = new BigNumber(signedOrders.length); -            const fillAmountWei = await forwarderWrapper.calculateMarketBuyFillAmountWeiAsync( -                signedOrders, -                feeOrders, -                feeProportion, -                makerAssetAmount, +            expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( +                erc20Balances[takerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount),              ); -            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); +            expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal( +                erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount).plus(wethSpentOnFeeOrders), +            ); +            expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT); +            expect(newBalances[forwarderContract.address][defaultMakerAssetAddress]).to.be.bignumber.equal( +                constants.ZERO_AMOUNT, +            ); +            expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);          }); -        it('buys ERC721 assets with fee abstraction', async () => { -            const makerAssetId = erc721MakerAssetIds[0]; -            signedOrder = await orderFactory.newSignedOrderAsync({ -                makerAssetAmount: new BigNumber(1), +        it('should buy the exact amount of assets when buying ZRX with fee abstraction', async () => { +            orderWithFee = await orderFactory.newSignedOrderAsync({ +                makerAssetData: assetProxyUtils.encodeERC20AssetData(zrxToken.address),                  takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT), -                makerAssetData: assetDataUtils.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, { +            ordersWithFee = [orderWithFee]; +            feeOrders = []; +            const makerAssetFillAmount = orderWithFee.makerAssetAmount.dividedToIntegerBy(2); +            const ethValue = orderWithFee.takerAssetAmount; +            tx = await forwarderWrapper.marketBuyOrdersWithEthAsync(ordersWithFee, feeOrders, makerAssetFillAmount, { +                value: ethValue,                  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 = await orderFactory.newSignedOrderAsync({ -                makerAssetAmount: new BigNumber(1), -                takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT), -                makerAssetData: assetDataUtils.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 takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress); +            const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address); +            const newBalances = await erc20Wrapper.getBalancesAsync(); + +            const primaryTakerAssetFillAmount = ForwarderWrapper.getWethForFeeOrders( +                makerAssetFillAmount, +                ordersWithFee,              ); -            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 = await orderFactory.newSignedOrderAsync({ -                makerAssetAmount: new BigNumber(1), -                takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), DECIMALS_DEFAULT), -                makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId1), -            }); -            const signedOrder2 = await orderFactory.newSignedOrderAsync({ -                makerAssetAmount: new BigNumber(1), -                takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), DECIMALS_DEFAULT), -                makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId2), -            }); -            signedOrders = [signedOrder1, signedOrder2]; -            feeProportion = 10; -            const makerAssetAmount = new BigNumber(signedOrders.length); -            const fillAmountWei = await forwarderWrapper.calculateMarketBuyFillAmountWeiAsync( -                signedOrders, -                feeOrders, -                feeProportion, -                makerAssetAmount, +            const totalEthSpent = primaryTakerAssetFillAmount.plus(DEFAULT_GAS_PRICE.times(tx.gasUsed)); +            const makerAssetFilledAmount = orderWithFee.makerAssetAmount +                .times(primaryTakerAssetFillAmount) +                .dividedToIntegerBy(orderWithFee.takerAssetAmount); +            const takerFeePaid = orderWithFee.takerFee +                .times(primaryTakerAssetFillAmount) +                .dividedToIntegerBy(orderWithFee.takerAssetAmount); +            const makerFeePaid = orderWithFee.makerFee +                .times(primaryTakerAssetFillAmount) +                .dividedToIntegerBy(orderWithFee.takerAssetAmount); + +            expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent)); +            expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal( +                erc20Balances[makerAddress][zrxToken.address].minus(makerAssetFilledAmount).minus(makerFeePaid),              ); -            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 = await orderFactory.newSignedOrderAsync({ -                makerAssetAmount: erc721MakerAssetAmount, -                takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), DECIMALS_DEFAULT), -                takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(6), DECIMALS_DEFAULT), -                makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId), -            }); -            signedOrders = [signedOrder]; -            const firstFeeOrder = await orderFactory.newSignedOrderAsync({ -                makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(8), DECIMALS_DEFAULT), -                takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(0.1), DECIMALS_DEFAULT), -                makerAssetData: assetDataUtils.encodeERC20AssetData(zrxToken.address), -                takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(0), DECIMALS_DEFAULT), -            }); -            const secondFeeOrder = await orderFactory.newSignedOrderAsync({ -                makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(8), DECIMALS_DEFAULT), -                takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(0.12), DECIMALS_DEFAULT), -                makerAssetData: assetDataUtils.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 = await orderFactory.newSignedOrderAsync({ -                makerAssetAmount: erc721MakerAssetAmount, -                takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), DECIMALS_DEFAULT), -                takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(6), DECIMALS_DEFAULT), -                makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId), -            }); -            const zrxMakerAssetAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(8), DECIMALS_DEFAULT); -            signedOrders = [signedOrder]; -            const firstFeeOrder = await orderFactory.newSignedOrderAsync({ -                makerAssetAmount: zrxMakerAssetAmount, -                takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(0.1), DECIMALS_DEFAULT), -                makerAssetData: assetDataUtils.encodeERC20AssetData(zrxToken.address), -                takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(0), DECIMALS_DEFAULT), -            }); -            const secondFeeOrder = await orderFactory.newSignedOrderAsync({ -                makerAssetAmount: zrxMakerAssetAmount, -                takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(0.12), DECIMALS_DEFAULT), -                makerAssetData: assetDataUtils.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, +            expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal( +                erc20Balances[takerAddress][zrxToken.address].plus(makerAssetFilledAmount).minus(takerFeePaid),              ); -            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 = await orderFactory.newSignedOrderAsync({ -                makerAssetAmount: new BigNumber(1), -                makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId), -            }); -            const erc20SignedOrder = await orderFactory.newSignedOrderAsync(); -            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, +            expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal( +                erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount),              ); -        }); -        it('throws when mixed ERC721 and ERC20 assets with ERC20 first', async () => { -            const makerAssetId = erc721MakerAssetIds[0]; -            const erc721SignedOrder = await orderFactory.newSignedOrderAsync({ -                makerAssetAmount: new BigNumber(1), -                makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId), -            }); -            const erc20SignedOrder = await orderFactory.newSignedOrderAsync(); -            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, +            expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT); +            expect(newBalances[forwarderContract.address][zrxToken.address]).to.be.bignumber.equal( +                erc20Balances[forwarderContract.address][zrxToken.address],              ); +            expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);          }); +        // it('throws if fees are higher than 5% when buying zrx', async () => { +        //     const highFeeZRXOrder = orderFactory.newSignedOrder({ +        //         makerAssetData: assetProxyUtils.encodeERC20AssetData(zrxToken.address), +        //         makerAssetAmount: orderWithoutFee.makerAssetAmount, +        //         takerFee: orderWithoutFee.makerAssetAmount.times(0.06), +        //     }); +        //     ordersWithFee = [highFeeZRXOrder]; +        //     feeOrders = []; +        //     const makerAssetAmount = orderWithoutFee.makerAssetAmount.div(2); +        //     const fillAmountWei = await forwarderWrapper.calculateMarketBuyFillAmountWeiAsync( +        //         ordersWithFee, +        //         feeOrders, +        //         feePercentage, +        //         makerAssetAmount, +        //     ); +        //     return expectTransactionFailedAsync( +        //         forwarderWrapper.marketBuyOrdersWithEthAsync(ordersWithFee, 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: orderWithoutFee.makerAssetAmount.times(0.06), +        //     }); +        //     ordersWithFee = [highFeeERC20Order]; +        //     feeOrders = [feeOrder]; +        //     const makerAssetAmount = orderWithoutFee.makerAssetAmount.div(2); +        //     const fillAmountWei = await forwarderWrapper.calculateMarketBuyFillAmountWeiAsync( +        //         ordersWithFee, +        //         feeOrders, +        //         feePercentage, +        //         makerAssetAmount, +        //     ); +        //     return expectTransactionFailedAsync( +        //         forwarderWrapper.marketBuyOrdersWithEthAsync(ordersWithFee, 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( +        //         ordersWithFee, +        //         feeOrders, +        //         feePercentage, +        //         makerAssetAmount, +        //     ); +        //     return expectTransactionFailedAsync( +        //         forwarderWrapper.marketBuyOrdersWithEthAsync(ordersWithFee, 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 = orderWithoutFee.makerAssetAmount; +        //     const primaryTakerAssetFillAmount = orderWithoutFee.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: orderWithoutFee.takerAssetAmount, +        //     }); +        //     await web3Wrapper.awaitTransactionSuccessAsync(wethDepositTxHash); +        //     // Transfer all of this WETH to the forwarding contract +        //     const wethTransferTxHash = await wethContract.transfer.sendTransactionAsync( +        //         forwarderContract.address, +        //         orderWithoutFee.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.marketBuyOrdersWithEth.sendTransactionAsync( +        //             formattedOrders.orders, +        //             formattedOrders.signatures, +        //             formattedFeeOrders.orders, +        //             formattedFeeOrders.signatures, +        //             makerAssetAmount, +        //             zero, +        //             constants.NULL_ADDRESS, +        //             { value: primaryTakerAssetFillAmount, from: takerAddress }, +        //         ), +        //         RevertReason.InvalidMsgValue, +        //     ); +        // });      }); +    // describe('marketBuyOrdersWithEth - ERC721', async () => { +    //     it('buys ERC721 assets', async () => { +    //         const makerAssetId = erc721MakerAssetIds[0]; +    //         orderWithoutFee = orderFactory.newSignedOrder({ +    //             makerAssetAmount: new BigNumber(1), +    //             makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId), +    //         }); +    //         feeOrders = []; +    //         signedOrders = [orderWithoutFee]; +    //         const makerAssetAmount = new BigNumber(signedOrders.length); +    //         const fillAmountWei = await forwarderWrapper.calculateMarketBuyFillAmountWeiAsync( +    //             signedOrders, +    //             feeOrders, +    //             feePercentage, +    //             makerAssetAmount, +    //         ); +    //         tx = await forwarderWrapper.marketBuyOrdersWithEthAsync(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]; +    //         orderWithoutFee = orderFactory.newSignedOrder({ +    //             makerAssetAmount: new BigNumber(1), +    //             takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT), +    //             makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId), +    //         }); +    //         signedOrders = [orderWithoutFee]; +    //         const makerAssetAmount = new BigNumber(signedOrders.length); +    //         const fillAmountWei = await forwarderWrapper.calculateMarketBuyFillAmountWeiAsync( +    //             signedOrders, +    //             feeOrders, +    //             feePercentage, +    //             makerAssetAmount, +    //         ); +    //         tx = await forwarderWrapper.marketBuyOrdersWithEthAsync(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]; +    //         orderWithoutFee = orderFactory.newSignedOrder({ +    //             makerAssetAmount: new BigNumber(1), +    //             takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT), +    //             makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId), +    //         }); +    //         signedOrders = [orderWithoutFee]; +    //         feePercentage = 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, +    //             feePercentage, +    //             makerAssetAmount, +    //         ); +    //         tx = await forwarderWrapper.marketBuyOrdersWithEthAsync( +    //             signedOrders, +    //             feeOrders, +    //             makerAssetAmount, +    //             { +    //                 from: takerAddress, +    //                 value: fillAmountWei, +    //                 gasPrice: DEFAULT_GAS_PRICE, +    //             }, +    //             { +    //                 feePercentage, +    //                 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]; +    //         feePercentage = 10; +    //         const makerAssetAmount = new BigNumber(signedOrders.length); +    //         const fillAmountWei = await forwarderWrapper.calculateMarketBuyFillAmountWeiAsync( +    //             signedOrders, +    //             feeOrders, +    //             feePercentage, +    //             makerAssetAmount, +    //         ); +    //         tx = await forwarderWrapper.marketBuyOrdersWithEthAsync(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]; +    //         feePercentage = 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); +    //         orderWithoutFee = 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 = [orderWithoutFee]; +    //         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, +    //             feePercentage, +    //             erc721MakerAssetAmount, +    //         ); +    //         // Simulate another otherAddress user partially filling firstFeeOrder +    //         const firstFeeOrderFillAmount = firstFeeOrder.makerAssetAmount.div(2); +    //         tx = await forwarderWrapper.marketBuyOrdersWithEthAsync([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, +    //             feePercentage, +    //             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.marketBuyOrdersWithEthAsync(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]; +    //         feePercentage = 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); +    //         orderWithoutFee = 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 = [orderWithoutFee]; +    //         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, +    //             feePercentage, +    //             erc721MakerAssetAmount, +    //         ); +    //         // Simulate another otherAddress user partially filling firstFeeOrder +    //         const firstFeeOrderFillAmount = firstFeeOrder.makerAssetAmount.div(2); +    //         tx = await forwarderWrapper.marketBuyOrdersWithEthAsync([firstFeeOrder], [], firstFeeOrderFillAmount, { +    //             from: otherAddress, +    //             value: fillAmountWei, +    //         }); +    //         const expectedFillAmountWei = await forwarderWrapper.calculateMarketBuyFillAmountWeiAsync( +    //             signedOrders, +    //             feeOrders, +    //             feePercentage, +    //             erc721MakerAssetAmount, +    //         ); +    //         tx = await forwarderWrapper.marketBuyOrdersWithEthAsync(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.marketBuyOrdersWithEthAsync(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.marketBuyOrdersWithEthAsync(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/utils/constants.ts b/packages/contracts/test/utils/constants.ts index 7dac38f56..65eaee398 100644 --- a/packages/contracts/test/utils/constants.ts +++ b/packages/contracts/test/utils/constants.ts @@ -49,4 +49,6 @@ export const constants = {          takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18),      },      WORD_LENGTH: 32, +    ZERO_AMOUNT: new BigNumber(0), +    PERCENTAGE_DENOMINATOR: new BigNumber(10).pow(18),  }; diff --git a/packages/contracts/test/utils/forwarder_wrapper.ts b/packages/contracts/test/utils/forwarder_wrapper.ts index e39df14b1..773ddf897 100644 --- a/packages/contracts/test/utils/forwarder_wrapper.ts +++ b/packages/contracts/test/utils/forwarder_wrapper.ts @@ -1,5 +1,4 @@ -import { assetDataUtils } from '@0xproject/order-utils'; -import { AssetProxyId, SignedOrder } from '@0xproject/types'; +import { SignedOrder } from '@0xproject/types';  import { BigNumber } from '@0xproject/utils';  import { Web3Wrapper } from '@0xproject/web3-wrapper';  import { Provider, TransactionReceiptWithDecodedLogs, TxDataPayable } from 'ethereum-types'; @@ -12,209 +11,108 @@ 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 readonly _web3Wrapper: Web3Wrapper;      private readonly _forwarderContract: ForwarderContract;      private readonly _logDecoder: LogDecoder; -    private readonly _zrxAddress: string; -    private static _createOptimizedSellOrders(signedOrders: SignedOrder[]): MarketSellOrders { -        const marketSellOrders = formatters.createMarketSellOrders(signedOrders, ZERO_AMOUNT); -        const assetDataId = assetDataUtils.decodeAssetProxyId(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; +    public static getPercentageOfValue(value: BigNumber, percentage: number): BigNumber { +        const numerator = constants.PERCENTAGE_DENOMINATOR.times(percentage).dividedToIntegerBy(100); +        const newValue = value.times(numerator).dividedToIntegerBy(constants.PERCENTAGE_DENOMINATOR); +        return newValue; +    } +    public static getWethForFeeOrders(feeAmount: BigNumber, feeOrders: SignedOrder[]): BigNumber { +        let wethAmount = new BigNumber(0); +        let remainingFeeAmount = feeAmount; +        _.forEach(feeOrders, feeOrder => { +            const feeAvailable = feeOrder.makerAssetAmount.minus(feeOrder.takerFee); +            if (!remainingFeeAmount.isZero() && feeAvailable.gte(remainingFeeAmount)) { +                wethAmount = wethAmount +                    .plus(feeOrder.takerAssetAmount.times(remainingFeeAmount).dividedToIntegerBy(feeAvailable)) +                    .plus(1); +                remainingFeeAmount = new BigNumber(0); +            } else if (!remainingFeeAmount.isZero()) { +                wethAmount = wethAmount.plus(feeOrder.takerAssetAmount).plus(1); +                remainingFeeAmount = remainingFeeAmount.minus(feeAvailable);              } -            marketSellOrders.orders[i].takerAssetData = constants.NULL_BYTES; -        } -        return marketSellOrders; +        }); +        return wethAmount;      } -    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 _createOptimizedOrders(signedOrders: SignedOrder[]): MarketSellOrders { +        _.forEach(signedOrders, (signedOrder, index) => { +            signedOrder.takerAssetData = constants.NULL_BYTES; +            if (index > 0) { +                signedOrder.makerAssetData = constants.NULL_BYTES; +            } +        }); +        const params = formatters.createMarketSellOrders(signedOrders, constants.ZERO_AMOUNT); +        return params;      } -    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; +    private static _createOptimizedZrxOrders(signedOrders: SignedOrder[]): MarketSellOrders { +        _.forEach(signedOrders, signedOrder => { +            signedOrder.makerAssetData = constants.NULL_BYTES; +            signedOrder.takerAssetData = constants.NULL_BYTES; +        }); +        const params = formatters.createMarketSellOrders(signedOrders, constants.ZERO_AMOUNT); +        return params;      } -    constructor(contractInstance: ForwarderContract, provider: Provider, zrxAddress: string) { +    constructor(contractInstance: ForwarderContract, provider: Provider) {          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( +    public async marketSellOrdersWithEthAsync(          orders: SignedOrder[],          feeOrders: SignedOrder[], -        makerTokenBuyAmount: BigNumber,          txData: TxDataPayable, -        opts: { feeProportion?: number; feeRecipient?: string } = {}, +        opts: { feePercentage?: BigNumber; 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 params = ForwarderWrapper._createOptimizedOrders(orders); +        const feeParams = ForwarderWrapper._createOptimizedZrxOrders(feeOrders); +        const feePercentage = _.isUndefined(opts.feePercentage) ? constants.ZERO_AMOUNT : opts.feePercentage;          const feeRecipient = _.isUndefined(opts.feeRecipient) ? constants.NULL_ADDRESS : opts.feeRecipient; -        const txHash: string = await this._forwarderContract.marketBuyTokensWithEth.sendTransactionAsync( +        const txHash = await this._forwarderContract.marketSellOrdersWithEth.sendTransactionAsync(              params.orders,              params.signatures,              feeParams.orders,              feeParams.signatures, -            makerTokenBuyAmount, -            feeProportion, +            feePercentage,              feeRecipient,              txData,          );          const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);          return tx;      } -    public async marketSellEthForERC20Async( +    public async marketBuyOrdersWithEthAsync(          orders: SignedOrder[],          feeOrders: SignedOrder[], +        makerAssetFillAmount: BigNumber,          txData: TxDataPayable, -        opts: { feeProportion?: number; feeRecipient?: string } = {}, +        opts: { feePercentage?: BigNumber; feeRecipient?: string } = {},      ): Promise<TransactionReceiptWithDecodedLogs> { -        const assetDataId = assetDataUtils.decodeAssetProxyId(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 params = ForwarderWrapper._createOptimizedOrders(orders); +        const feeParams = ForwarderWrapper._createOptimizedZrxOrders(feeOrders); +        const feePercentage = _.isUndefined(opts.feePercentage) ? constants.ZERO_AMOUNT : opts.feePercentage;          const feeRecipient = _.isUndefined(opts.feeRecipient) ? constants.NULL_ADDRESS : opts.feeRecipient; -        const txHash: string = await this._forwarderContract.marketSellEthForERC20.sendTransactionAsync( +        const txHash = await this._forwarderContract.marketBuyOrdersWithEth.sendTransactionAsync(              params.orders, +            makerAssetFillAmount,              params.signatures,              feeParams.orders,              feeParams.signatures, -            feeProportion, +            feePercentage,              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 = assetDataUtils.decodeAssetProxyId(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 = assetDataUtils.decodeAssetDataOrThrow(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; +    public async withdrawERC20Async( +        tokenAddress: string, +        amount: BigNumber, +        txData: TxDataPayable, +    ): Promise<TransactionReceiptWithDecodedLogs> { +        const txHash = await this._forwarderContract.withdrawERC20.sendTransactionAsync(tokenAddress, amount, txData); +        const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); +        return tx;      }  } diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 19f2b1a23..b96bdb182 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -216,7 +216,7 @@ export enum RevertReason {      Erc20InsufficientBalance = 'ERC20_INSUFFICIENT_BALANCE',      Erc20InsufficientAllowance = 'ERC20_INSUFFICIENT_ALLOWANCE',      UnacceptableThreshold = 'UNACCEPTABLE_THRESHOLD', -    FeeProportionTooLarge = 'FEE_PROPORTION_TOO_LARGE', +    FeePercentageTooLarge = 'FEE_PERCENTAGE_TOO_LARGE',      ValueGreaterThanZero = 'VALUE_GREATER_THAN_ZERO',      InvalidMsgValue = 'INVALID_MSG_VALUE',  } | 
