update contracts and tests to support different maker assets
This commit is contained in:
parent
0ff8b12770
commit
8077123e9f
@ -27,10 +27,12 @@ import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
|
||||
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||
import "./libs/LibConstants.sol";
|
||||
import "./libs/LibForwarderRichErrors.sol";
|
||||
import "./MixinAssets.sol";
|
||||
|
||||
|
||||
contract MixinExchangeWrapper is
|
||||
LibConstants
|
||||
LibConstants,
|
||||
MixinAssets
|
||||
{
|
||||
using LibSafeMath for uint256;
|
||||
|
||||
@ -72,14 +74,12 @@ contract MixinExchangeWrapper is
|
||||
/// @param order A single order specification.
|
||||
/// @param signature Signature for the given order.
|
||||
/// @param remainingTakerAssetFillAmount Remaining amount of WETH to sell.
|
||||
/// @param protocolFee Amount of WETH that will be spent on the protocol fee for each order.
|
||||
/// @return wethSpentAmount Amount of WETH spent on the given order.
|
||||
/// @return makerAssetAcquiredAmount Amount of maker asset acquired from the given order.
|
||||
function _marketSellSingleOrder(
|
||||
LibOrder.Order memory order,
|
||||
bytes memory signature,
|
||||
uint256 remainingTakerAssetFillAmount,
|
||||
uint256 protocolFee
|
||||
uint256 remainingTakerAssetFillAmount
|
||||
)
|
||||
internal
|
||||
returns (
|
||||
@ -92,7 +92,7 @@ contract MixinExchangeWrapper is
|
||||
// Attempt to sell the remaining amount of WETH
|
||||
LibFillResults.FillResults memory singleFillResults = _fillOrderNoThrow(
|
||||
order,
|
||||
remainingTakerAssetFillAmount.safeSub(protocolFee),
|
||||
remainingTakerAssetFillAmount,
|
||||
signature
|
||||
);
|
||||
|
||||
@ -110,7 +110,7 @@ contract MixinExchangeWrapper is
|
||||
uint256 takerAssetFillAmount = LibMath.getPartialAmountCeil(
|
||||
order.takerAssetAmount,
|
||||
order.takerAssetAmount.safeAdd(order.takerFee),
|
||||
remainingTakerAssetFillAmount.safeSub(protocolFee)
|
||||
remainingTakerAssetFillAmount
|
||||
);
|
||||
|
||||
LibFillResults.FillResults memory singleFillResults = _fillOrderNoThrow(
|
||||
@ -161,7 +161,8 @@ contract MixinExchangeWrapper is
|
||||
|
||||
// The remaining amount of WETH to sell
|
||||
uint256 remainingTakerAssetFillAmount = wethSellAmount
|
||||
.safeSub(totalWethSpentAmount);
|
||||
.safeSub(totalWethSpentAmount)
|
||||
.safeSub(protocolFee);
|
||||
|
||||
(
|
||||
uint256 wethSpentAmount,
|
||||
@ -169,10 +170,11 @@ contract MixinExchangeWrapper is
|
||||
) = _marketSellSingleOrder(
|
||||
orders[i],
|
||||
signatures[i],
|
||||
remainingTakerAssetFillAmount,
|
||||
protocolFee
|
||||
remainingTakerAssetFillAmount
|
||||
);
|
||||
|
||||
_transferAssetToSender(orders[i].makerAssetData, makerAssetAcquiredAmount);
|
||||
|
||||
totalWethSpentAmount = totalWethSpentAmount
|
||||
.safeAdd(wethSpentAmount);
|
||||
totalMakerAssetAcquiredAmount = totalMakerAssetAcquiredAmount
|
||||
@ -294,6 +296,8 @@ contract MixinExchangeWrapper is
|
||||
remainingMakerAssetFillAmount
|
||||
);
|
||||
|
||||
_transferAssetToSender(orders[i].makerAssetData, makerAssetAcquiredAmount);
|
||||
|
||||
totalWethSpentAmount = totalWethSpentAmount
|
||||
.safeAdd(wethSpentAmount);
|
||||
totalMakerAssetAcquiredAmount = totalMakerAssetAcquiredAmount
|
||||
|
@ -28,7 +28,6 @@ import "./libs/LibConstants.sol";
|
||||
import "./libs/LibForwarderRichErrors.sol";
|
||||
import "./interfaces/IAssets.sol";
|
||||
import "./interfaces/IForwarderCore.sol";
|
||||
import "./MixinAssets.sol";
|
||||
import "./MixinExchangeWrapper.sol";
|
||||
import "./MixinWeth.sol";
|
||||
|
||||
@ -38,7 +37,6 @@ contract MixinForwarderCore is
|
||||
IAssets,
|
||||
IForwarderCore,
|
||||
MixinWeth,
|
||||
MixinAssets,
|
||||
MixinExchangeWrapper
|
||||
{
|
||||
using LibBytes for bytes;
|
||||
@ -93,7 +91,8 @@ contract MixinForwarderCore is
|
||||
msg.value
|
||||
);
|
||||
|
||||
// Spends up to wethSellAmount to fill orders and pay WETH order fees.
|
||||
// Spends up to wethSellAmount to fill orders, transfers purchased assets to msg.sender,
|
||||
// and pays WETH order fees.
|
||||
(
|
||||
wethSpentAmount,
|
||||
makerAssetAcquiredAmount
|
||||
@ -110,12 +109,6 @@ contract MixinForwarderCore is
|
||||
feePercentage,
|
||||
feeRecipient
|
||||
);
|
||||
|
||||
// Transfer purchased assets to msg.sender.
|
||||
_transferAssetToSender(
|
||||
orders[0].makerAssetData,
|
||||
makerAssetAcquiredAmount
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Attempt to buy makerAssetBuyAmount of makerAsset by selling ETH provided with transaction.
|
||||
@ -148,9 +141,7 @@ contract MixinForwarderCore is
|
||||
// Convert ETH to WETH.
|
||||
_convertEthToWeth();
|
||||
|
||||
// Attempt to fill the desired amount of makerAsset. Note that makerAssetAcquiredAmount < makerAssetBuyAmount
|
||||
// if any of the orders filled have an takerFee denominated in makerAsset, since these fees will be paid out
|
||||
// from the Forwarder's temporary makerAsset balance.
|
||||
// Attempts to fill the desired amount of makerAsset and trasnfer purchased assets to msg.sender.
|
||||
(
|
||||
wethSpentAmount,
|
||||
makerAssetAcquiredAmount
|
||||
@ -167,11 +158,5 @@ contract MixinForwarderCore is
|
||||
feePercentage,
|
||||
feeRecipient
|
||||
);
|
||||
|
||||
// Transfer acquired assets to msg.sender.
|
||||
_transferAssetToSender(
|
||||
orders[0].makerAssetData,
|
||||
makerAssetAcquiredAmount
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -154,7 +154,7 @@ blockchainTests(ContractName.Forwarder, env => {
|
||||
wethAssetData,
|
||||
);
|
||||
forwarderWrapper = new ForwarderWrapper(forwarderContract, env.provider);
|
||||
await forwarderWrapper.approveMakerAssetProxyAsync(assetDataUtils.encodeERC20AssetData(erc20Token.address), {
|
||||
await forwarderWrapper.approveMakerAssetProxyAsync(defaultOrderParams.makerAssetData, {
|
||||
from: takerAddress,
|
||||
});
|
||||
erc20Wrapper.addTokenOwnerAddress(forwarderContract.address);
|
||||
@ -203,7 +203,7 @@ blockchainTests(ContractName.Forwarder, env => {
|
||||
blockchainTests.resets('marketSellOrdersWithEth without extra fees', () => {
|
||||
it('should fill a single order without a taker fee', async () => {
|
||||
const orderWithoutFee = await orderFactory.newSignedOrderAsync();
|
||||
await forwarderTestFactory.marketSellTestAsync([orderWithoutFee], 0.78, erc20Token);
|
||||
await forwarderTestFactory.marketSellTestAsync([orderWithoutFee], 0.78, [erc20Token]);
|
||||
});
|
||||
it('should fill multiple orders without taker fees', async () => {
|
||||
const firstOrder = await orderFactory.newSignedOrderAsync();
|
||||
@ -212,14 +212,14 @@ blockchainTests(ContractName.Forwarder, env => {
|
||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(21, DECIMALS_DEFAULT),
|
||||
});
|
||||
const orders = [firstOrder, secondOrder];
|
||||
await forwarderTestFactory.marketSellTestAsync(orders, 1.51, erc20Token);
|
||||
await forwarderTestFactory.marketSellTestAsync(orders, 1.51, [erc20Token]);
|
||||
});
|
||||
it('should fill a single order with a percentage fee', async () => {
|
||||
const orderWithPercentageFee = await orderFactory.newSignedOrderAsync({
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
|
||||
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
|
||||
});
|
||||
await forwarderTestFactory.marketSellTestAsync([orderWithPercentageFee], 0.58, erc20Token);
|
||||
await forwarderTestFactory.marketSellTestAsync([orderWithPercentageFee], 0.58, [erc20Token]);
|
||||
});
|
||||
it('should fill multiple orders with percentage fees', async () => {
|
||||
const firstOrder = await orderFactory.newSignedOrderAsync({
|
||||
@ -233,7 +233,7 @@ blockchainTests(ContractName.Forwarder, env => {
|
||||
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
|
||||
});
|
||||
const orders = [firstOrder, secondOrder];
|
||||
await forwarderTestFactory.marketSellTestAsync(orders, 1.34, erc20Token);
|
||||
await forwarderTestFactory.marketSellTestAsync(orders, 1.34, [erc20Token]);
|
||||
});
|
||||
it('should fail to fill an order with a percentage fee if the asset proxy is not yet approved', async () => {
|
||||
const unapprovedAsset = assetDataUtils.encodeERC20AssetData(secondErc20Token.address);
|
||||
@ -280,7 +280,7 @@ blockchainTests(ContractName.Forwarder, env => {
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
|
||||
takerFeeAssetData: wethAssetData,
|
||||
});
|
||||
await forwarderTestFactory.marketSellTestAsync([orderWithWethFee], 0.13, erc20Token);
|
||||
await forwarderTestFactory.marketSellTestAsync([orderWithWethFee], 0.13, [erc20Token]);
|
||||
});
|
||||
it('should fill multiple orders with WETH fees', async () => {
|
||||
const firstOrder = await orderFactory.newSignedOrderAsync({
|
||||
@ -294,7 +294,7 @@ blockchainTests(ContractName.Forwarder, env => {
|
||||
takerFeeAssetData: wethAssetData,
|
||||
});
|
||||
const orders = [firstOrder, secondOrderWithWethFee];
|
||||
await forwarderTestFactory.marketSellTestAsync(orders, 1.25, erc20Token);
|
||||
await forwarderTestFactory.marketSellTestAsync(orders, 1.25, [erc20Token]);
|
||||
});
|
||||
it('should refund remaining ETH if amount is greater than takerAssetAmount', async () => {
|
||||
const order = await orderFactory.newSignedOrderAsync();
|
||||
@ -310,6 +310,21 @@ blockchainTests(ContractName.Forwarder, env => {
|
||||
|
||||
expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
|
||||
});
|
||||
it('should fill orders with different makerAssetData', async () => {
|
||||
const firstOrderMakerAssetData = assetDataUtils.encodeERC20AssetData(erc20Token.address);
|
||||
const firstOrder = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetData: firstOrderMakerAssetData,
|
||||
});
|
||||
|
||||
const secondOrderMakerAssetData = assetDataUtils.encodeERC20AssetData(secondErc20Token.address);
|
||||
const secondOrder = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetData: secondOrderMakerAssetData,
|
||||
});
|
||||
await forwarderWrapper.approveMakerAssetProxyAsync(secondOrderMakerAssetData, { from: takerAddress });
|
||||
|
||||
const orders = [firstOrder, secondOrder];
|
||||
await forwarderTestFactory.marketSellTestAsync(orders, 1.5, [erc20Token, secondErc20Token]);
|
||||
});
|
||||
it('should fail to fill an order with a fee denominated in an asset other than makerAsset or WETH', async () => {
|
||||
const makerAssetData = assetDataUtils.encodeERC20AssetData(erc20Token.address);
|
||||
const takerFeeAssetData = assetDataUtils.encodeERC20AssetData(secondErc20Token.address);
|
||||
@ -321,14 +336,14 @@ blockchainTests(ContractName.Forwarder, env => {
|
||||
});
|
||||
|
||||
const revertError = new ForwarderRevertErrors.UnsupportedFeeError(takerFeeAssetData);
|
||||
await forwarderTestFactory.marketSellTestAsync([order], 0.5, erc20Token, {
|
||||
await forwarderTestFactory.marketSellTestAsync([order], 0.5, [erc20Token], {
|
||||
revertError,
|
||||
});
|
||||
});
|
||||
it('should fill a partially-filled order without a taker fee', async () => {
|
||||
const order = await orderFactory.newSignedOrderAsync();
|
||||
await forwarderTestFactory.marketSellTestAsync([order], 0.3, erc20Token);
|
||||
await forwarderTestFactory.marketSellTestAsync([order], 0.8, erc20Token);
|
||||
await forwarderTestFactory.marketSellTestAsync([order], 0.3, [erc20Token]);
|
||||
await forwarderTestFactory.marketSellTestAsync([order], 0.8, [erc20Token]);
|
||||
});
|
||||
it('should skip over an order with an invalid maker asset amount', async () => {
|
||||
const unfillableOrder = await orderFactory.newSignedOrderAsync({
|
||||
@ -336,7 +351,7 @@ blockchainTests(ContractName.Forwarder, env => {
|
||||
});
|
||||
const fillableOrder = await orderFactory.newSignedOrderAsync();
|
||||
|
||||
await forwarderTestFactory.marketSellTestAsync([unfillableOrder, fillableOrder], 1.5, erc20Token);
|
||||
await forwarderTestFactory.marketSellTestAsync([unfillableOrder, fillableOrder], 1.5, [erc20Token]);
|
||||
});
|
||||
it('should skip over an order with an invalid taker asset amount', async () => {
|
||||
const unfillableOrder = await orderFactory.newSignedOrderAsync({
|
||||
@ -344,7 +359,7 @@ blockchainTests(ContractName.Forwarder, env => {
|
||||
});
|
||||
const fillableOrder = await orderFactory.newSignedOrderAsync();
|
||||
|
||||
await forwarderTestFactory.marketSellTestAsync([unfillableOrder, fillableOrder], 1.5, erc20Token);
|
||||
await forwarderTestFactory.marketSellTestAsync([unfillableOrder, fillableOrder], 1.5, [erc20Token]);
|
||||
});
|
||||
it('should skip over an expired order', async () => {
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
@ -353,21 +368,21 @@ blockchainTests(ContractName.Forwarder, env => {
|
||||
});
|
||||
const fillableOrder = await orderFactory.newSignedOrderAsync();
|
||||
|
||||
await forwarderTestFactory.marketSellTestAsync([expiredOrder, fillableOrder], 1.5, erc20Token);
|
||||
await forwarderTestFactory.marketSellTestAsync([expiredOrder, fillableOrder], 1.5, [erc20Token]);
|
||||
});
|
||||
it('should skip over a fully filled order', async () => {
|
||||
const fullyFilledOrder = await orderFactory.newSignedOrderAsync();
|
||||
await forwarderTestFactory.marketSellTestAsync([fullyFilledOrder], 1, erc20Token);
|
||||
await forwarderTestFactory.marketSellTestAsync([fullyFilledOrder], 1, [erc20Token]);
|
||||
|
||||
const fillableOrder = await orderFactory.newSignedOrderAsync();
|
||||
await forwarderTestFactory.marketSellTestAsync([fullyFilledOrder, fillableOrder], 1.5, erc20Token);
|
||||
await forwarderTestFactory.marketSellTestAsync([fullyFilledOrder, fillableOrder], 1.5, [erc20Token]);
|
||||
});
|
||||
it('should skip over a cancelled order', async () => {
|
||||
const cancelledOrder = await orderFactory.newSignedOrderAsync();
|
||||
await exchangeWrapper.cancelOrderAsync(cancelledOrder, makerAddress);
|
||||
|
||||
const fillableOrder = await orderFactory.newSignedOrderAsync();
|
||||
await forwarderTestFactory.marketSellTestAsync([cancelledOrder, fillableOrder], 1.5, erc20Token);
|
||||
await forwarderTestFactory.marketSellTestAsync([cancelledOrder, fillableOrder], 1.5, [erc20Token]);
|
||||
});
|
||||
});
|
||||
blockchainTests.resets('marketSellOrdersWithEth with extra fees', () => {
|
||||
@ -376,7 +391,7 @@ blockchainTests(ContractName.Forwarder, env => {
|
||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(157, DECIMALS_DEFAULT),
|
||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(36, DECIMALS_DEFAULT),
|
||||
});
|
||||
await forwarderTestFactory.marketSellTestAsync([order], 0.67, erc20Token, {
|
||||
await forwarderTestFactory.marketSellTestAsync([order], 0.67, [erc20Token], {
|
||||
forwarderFeePercentage: new BigNumber(2),
|
||||
});
|
||||
});
|
||||
@ -387,7 +402,7 @@ blockchainTests(ContractName.Forwarder, env => {
|
||||
ForwarderTestFactory.getPercentageOfValue(constants.PERCENTAGE_DENOMINATOR, forwarderFeePercentage),
|
||||
);
|
||||
|
||||
await forwarderTestFactory.marketSellTestAsync([order], 0.5, erc20Token, {
|
||||
await forwarderTestFactory.marketSellTestAsync([order], 0.5, [erc20Token], {
|
||||
forwarderFeePercentage,
|
||||
revertError,
|
||||
});
|
||||
@ -399,7 +414,7 @@ blockchainTests(ContractName.Forwarder, env => {
|
||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(131, DECIMALS_DEFAULT),
|
||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(20, DECIMALS_DEFAULT),
|
||||
});
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.62, erc20Token);
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.62, [erc20Token]);
|
||||
});
|
||||
it('should buy the exact amount of makerAsset in multiple orders', async () => {
|
||||
const firstOrder = await orderFactory.newSignedOrderAsync();
|
||||
@ -408,14 +423,29 @@ blockchainTests(ContractName.Forwarder, env => {
|
||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(11, DECIMALS_DEFAULT),
|
||||
});
|
||||
const orders = [firstOrder, secondOrder];
|
||||
await forwarderTestFactory.marketBuyTestAsync(orders, 1.96, erc20Token);
|
||||
await forwarderTestFactory.marketBuyTestAsync(orders, 1.96, [erc20Token]);
|
||||
});
|
||||
it('should buy exactly makerAssetBuyAmount in orders with different makerAssetData', async () => {
|
||||
const firstOrderMakerAssetData = assetDataUtils.encodeERC20AssetData(erc20Token.address);
|
||||
const firstOrder = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetData: firstOrderMakerAssetData,
|
||||
});
|
||||
|
||||
const secondOrderMakerAssetData = assetDataUtils.encodeERC20AssetData(secondErc20Token.address);
|
||||
const secondOrder = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetData: secondOrderMakerAssetData,
|
||||
});
|
||||
await forwarderWrapper.approveMakerAssetProxyAsync(secondOrderMakerAssetData, { from: takerAddress });
|
||||
|
||||
const orders = [firstOrder, secondOrder];
|
||||
await forwarderTestFactory.marketBuyTestAsync(orders, 1.5, [erc20Token, secondErc20Token]);
|
||||
});
|
||||
it('should buy the exact amount of makerAsset and return excess ETH', async () => {
|
||||
const order = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(80, DECIMALS_DEFAULT),
|
||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(17, DECIMALS_DEFAULT),
|
||||
});
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.57, erc20Token, {
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.57, [erc20Token], {
|
||||
ethValueAdjustment: 2,
|
||||
});
|
||||
});
|
||||
@ -426,7 +456,7 @@ blockchainTests(ContractName.Forwarder, env => {
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
|
||||
takerFeeAssetData: wethAssetData,
|
||||
});
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.38, erc20Token);
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.38, [erc20Token]);
|
||||
});
|
||||
it('should buy the exact amount of makerAsset from a single order with a percentage fee', async () => {
|
||||
const order = await orderFactory.newSignedOrderAsync({
|
||||
@ -435,7 +465,7 @@ blockchainTests(ContractName.Forwarder, env => {
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
|
||||
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
|
||||
});
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.52, erc20Token);
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.52, [erc20Token]);
|
||||
});
|
||||
it('should revert if the amount of ETH sent is too low to fill the makerAssetAmount', async () => {
|
||||
const order = await orderFactory.newSignedOrderAsync();
|
||||
@ -444,7 +474,7 @@ blockchainTests(ContractName.Forwarder, env => {
|
||||
constants.ZERO_AMOUNT,
|
||||
);
|
||||
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.5, erc20Token, {
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.5, [erc20Token], {
|
||||
ethValueAdjustment: -2,
|
||||
revertError,
|
||||
});
|
||||
@ -456,7 +486,7 @@ blockchainTests(ContractName.Forwarder, env => {
|
||||
makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
|
||||
takerFeeAssetData: wethAssetData,
|
||||
});
|
||||
await forwarderTestFactory.marketBuyTestAsync([erc721Order], 1, erc721Token, {
|
||||
await forwarderTestFactory.marketBuyTestAsync([erc721Order], 1, [erc721Token], {
|
||||
makerAssetId,
|
||||
});
|
||||
});
|
||||
@ -468,7 +498,7 @@ blockchainTests(ContractName.Forwarder, env => {
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(1, DECIMALS_DEFAULT),
|
||||
takerFeeAssetData: wethAssetData,
|
||||
});
|
||||
await forwarderTestFactory.marketBuyTestAsync([erc721orderWithWethFee], 1, erc721Token, {
|
||||
await forwarderTestFactory.marketBuyTestAsync([erc721orderWithWethFee], 1, [erc721Token], {
|
||||
makerAssetId,
|
||||
});
|
||||
});
|
||||
@ -483,14 +513,14 @@ blockchainTests(ContractName.Forwarder, env => {
|
||||
});
|
||||
|
||||
const revertError = new ForwarderRevertErrors.UnsupportedFeeError(takerFeeAssetData);
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.5, erc20Token, {
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.5, [erc20Token], {
|
||||
revertError,
|
||||
});
|
||||
});
|
||||
it('should fill a partially-filled order without a taker fee', async () => {
|
||||
const order = await orderFactory.newSignedOrderAsync();
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.3, erc20Token);
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.8, erc20Token);
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.3, [erc20Token]);
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.8, [erc20Token]);
|
||||
});
|
||||
it('should skip over an order with an invalid maker asset amount', async () => {
|
||||
const unfillableOrder = await orderFactory.newSignedOrderAsync({
|
||||
@ -498,7 +528,7 @@ blockchainTests(ContractName.Forwarder, env => {
|
||||
});
|
||||
const fillableOrder = await orderFactory.newSignedOrderAsync();
|
||||
|
||||
await forwarderTestFactory.marketBuyTestAsync([unfillableOrder, fillableOrder], 1.5, erc20Token);
|
||||
await forwarderTestFactory.marketBuyTestAsync([unfillableOrder, fillableOrder], 1.5, [erc20Token]);
|
||||
});
|
||||
it('should skip over an order with an invalid taker asset amount', async () => {
|
||||
const unfillableOrder = await orderFactory.newSignedOrderAsync({
|
||||
@ -506,7 +536,7 @@ blockchainTests(ContractName.Forwarder, env => {
|
||||
});
|
||||
const fillableOrder = await orderFactory.newSignedOrderAsync();
|
||||
|
||||
await forwarderTestFactory.marketBuyTestAsync([unfillableOrder, fillableOrder], 1.5, erc20Token);
|
||||
await forwarderTestFactory.marketBuyTestAsync([unfillableOrder, fillableOrder], 1.5, [erc20Token]);
|
||||
});
|
||||
it('should skip over an expired order', async () => {
|
||||
const currentTimestamp = await getLatestBlockTimestampAsync();
|
||||
@ -515,21 +545,21 @@ blockchainTests(ContractName.Forwarder, env => {
|
||||
});
|
||||
const fillableOrder = await orderFactory.newSignedOrderAsync();
|
||||
|
||||
await forwarderTestFactory.marketBuyTestAsync([expiredOrder, fillableOrder], 1.5, erc20Token);
|
||||
await forwarderTestFactory.marketBuyTestAsync([expiredOrder, fillableOrder], 1.5, [erc20Token]);
|
||||
});
|
||||
it('should skip over a fully filled order', async () => {
|
||||
const fullyFilledOrder = await orderFactory.newSignedOrderAsync();
|
||||
await forwarderTestFactory.marketBuyTestAsync([fullyFilledOrder], 1, erc20Token);
|
||||
await forwarderTestFactory.marketBuyTestAsync([fullyFilledOrder], 1, [erc20Token]);
|
||||
|
||||
const fillableOrder = await orderFactory.newSignedOrderAsync();
|
||||
await forwarderTestFactory.marketBuyTestAsync([fullyFilledOrder, fillableOrder], 1.5, erc20Token);
|
||||
await forwarderTestFactory.marketBuyTestAsync([fullyFilledOrder, fillableOrder], 1.5, [erc20Token]);
|
||||
});
|
||||
it('should skip over a cancelled order', async () => {
|
||||
const cancelledOrder = await orderFactory.newSignedOrderAsync();
|
||||
await exchangeWrapper.cancelOrderAsync(cancelledOrder, makerAddress);
|
||||
|
||||
const fillableOrder = await orderFactory.newSignedOrderAsync();
|
||||
await forwarderTestFactory.marketBuyTestAsync([cancelledOrder, fillableOrder], 1.5, erc20Token);
|
||||
await forwarderTestFactory.marketBuyTestAsync([cancelledOrder, fillableOrder], 1.5, [erc20Token]);
|
||||
});
|
||||
it('Should buy slightly greater makerAsset when exchange rate is rounded', async () => {
|
||||
// The 0x Protocol contracts round the exchange rate in favor of the Maker.
|
||||
@ -649,7 +679,7 @@ blockchainTests(ContractName.Forwarder, env => {
|
||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(125, DECIMALS_DEFAULT),
|
||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(11, DECIMALS_DEFAULT),
|
||||
});
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.33, erc20Token, {
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.33, [erc20Token], {
|
||||
forwarderFeePercentage: new BigNumber(2),
|
||||
});
|
||||
});
|
||||
@ -658,7 +688,7 @@ blockchainTests(ContractName.Forwarder, env => {
|
||||
const revertError = new ForwarderRevertErrors.FeePercentageTooLargeError(
|
||||
ForwarderTestFactory.getPercentageOfValue(constants.PERCENTAGE_DENOMINATOR, new BigNumber(6)),
|
||||
);
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.5, erc20Token, {
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.5, [erc20Token], {
|
||||
forwarderFeePercentage: new BigNumber(6),
|
||||
revertError,
|
||||
});
|
||||
@ -674,7 +704,7 @@ blockchainTests(ContractName.Forwarder, env => {
|
||||
const revertError = new ForwarderRevertErrors.InsufficientEthForFeeError(ethFee, ethFee.minus(1));
|
||||
|
||||
// -2 to compensate for the extra 1 wei added in ForwarderTestFactory to account for rounding
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.5, erc20Token, {
|
||||
await forwarderTestFactory.marketBuyTestAsync([order], 0.5, [erc20Token], {
|
||||
ethValueAdjustment: -2,
|
||||
forwarderFeePercentage,
|
||||
revertError,
|
||||
|
@ -3,6 +3,7 @@ import { DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import { DummyERC721TokenContract } from '@0x/contracts-erc721';
|
||||
import { ExchangeWrapper } from '@0x/contracts-exchange';
|
||||
import { constants, ERC20BalancesByOwner, expect, OrderStatus, web3Wrapper } from '@0x/contracts-test-utils';
|
||||
import { assetDataUtils } from '@0x/order-utils';
|
||||
import { OrderInfo, SignedOrder } from '@0x/types';
|
||||
import { BigNumber, RevertError } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
@ -12,10 +13,14 @@ import { ForwarderWrapper } from './forwarder_wrapper';
|
||||
// Necessary bookkeeping to validate Forwarder results
|
||||
interface ForwarderFillState {
|
||||
takerAssetFillAmount: BigNumber;
|
||||
makerAssetFillAmount: BigNumber;
|
||||
makerAssetFillAmount: {
|
||||
[makerAssetData: string]: BigNumber;
|
||||
};
|
||||
protocolFees: BigNumber;
|
||||
wethFees: BigNumber;
|
||||
percentageFees: BigNumber;
|
||||
percentageFees: {
|
||||
[makerAssetData: string]: BigNumber;
|
||||
};
|
||||
maxOversoldWeth: BigNumber;
|
||||
maxOverboughtMakerAsset: BigNumber;
|
||||
}
|
||||
@ -51,7 +56,7 @@ export class ForwarderTestFactory {
|
||||
public async marketBuyTestAsync(
|
||||
orders: SignedOrder[],
|
||||
fractionalNumberOfOrdersToFill: number,
|
||||
makerAssetContract: DummyERC20TokenContract | DummyERC721TokenContract,
|
||||
makerAssetContracts: Array<DummyERC20TokenContract | DummyERC721TokenContract>,
|
||||
options: {
|
||||
ethValueAdjustment?: number; // Used to provided insufficient/excess ETH
|
||||
forwarderFeePercentage?: BigNumber;
|
||||
@ -83,9 +88,16 @@ export class ForwarderTestFactory {
|
||||
constants.PERCENTAGE_DENOMINATOR,
|
||||
forwarderFeePercentage,
|
||||
);
|
||||
|
||||
const totalMakerAssetFillAmount = Object.values(expectedResults.makerAssetFillAmount).reduce((prev, current) =>
|
||||
prev.plus(current),
|
||||
);
|
||||
const totalPercentageFees = Object.values(expectedResults.percentageFees).reduce((prev, current) =>
|
||||
prev.plus(current),
|
||||
);
|
||||
const tx = this._forwarderWrapper.marketBuyOrdersWithEthAsync(
|
||||
orders,
|
||||
expectedResults.makerAssetFillAmount.minus(expectedResults.percentageFees),
|
||||
totalMakerAssetFillAmount.minus(totalPercentageFees),
|
||||
{
|
||||
value: ethValue,
|
||||
from: this._takerAddress,
|
||||
@ -110,7 +122,7 @@ export class ForwarderTestFactory {
|
||||
expectedResults,
|
||||
takerEthBalanceBefore,
|
||||
erc20Balances,
|
||||
makerAssetContract,
|
||||
makerAssetContracts,
|
||||
{
|
||||
forwarderFeePercentage,
|
||||
forwarderFeeRecipientEthBalanceBefore,
|
||||
@ -123,7 +135,7 @@ export class ForwarderTestFactory {
|
||||
public async marketSellTestAsync(
|
||||
orders: SignedOrder[],
|
||||
fractionalNumberOfOrdersToFill: number,
|
||||
makerAssetContract: DummyERC20TokenContract,
|
||||
makerAssetContracts: DummyERC20TokenContract[],
|
||||
options: {
|
||||
forwarderFeePercentage?: BigNumber;
|
||||
revertError?: RevertError;
|
||||
@ -179,7 +191,7 @@ export class ForwarderTestFactory {
|
||||
expectedResults,
|
||||
takerEthBalanceBefore,
|
||||
erc20Balances,
|
||||
makerAssetContract,
|
||||
makerAssetContracts,
|
||||
{
|
||||
forwarderFeePercentage,
|
||||
forwarderFeeRecipientEthBalanceBefore,
|
||||
@ -195,31 +207,35 @@ export class ForwarderTestFactory {
|
||||
makerAssetContract: DummyERC20TokenContract,
|
||||
): void {
|
||||
const makerAssetAddress = makerAssetContract.address;
|
||||
const makerAssetData = assetDataUtils.encodeERC20AssetData(makerAssetAddress);
|
||||
|
||||
const {
|
||||
maxOverboughtMakerAsset,
|
||||
makerAssetFillAmount: { [makerAssetData]: makerAssetFillAmount },
|
||||
percentageFees: { [makerAssetData]: percentageFees },
|
||||
} = expectedResults;
|
||||
|
||||
expectBalanceWithin(
|
||||
newBalances[this._makerAddress][makerAssetAddress],
|
||||
oldBalances[this._makerAddress][makerAssetAddress]
|
||||
.minus(expectedResults.makerAssetFillAmount)
|
||||
.minus(expectedResults.maxOverboughtMakerAsset),
|
||||
oldBalances[this._makerAddress][makerAssetAddress].minus(expectedResults.makerAssetFillAmount),
|
||||
.minus(makerAssetFillAmount)
|
||||
.minus(maxOverboughtMakerAsset),
|
||||
oldBalances[this._makerAddress][makerAssetAddress].minus(makerAssetFillAmount),
|
||||
'Maker makerAsset balance',
|
||||
);
|
||||
expectBalanceWithin(
|
||||
newBalances[this._takerAddress][makerAssetAddress],
|
||||
oldBalances[this._takerAddress][makerAssetAddress].plus(makerAssetFillAmount).minus(percentageFees),
|
||||
oldBalances[this._takerAddress][makerAssetAddress]
|
||||
.plus(expectedResults.makerAssetFillAmount)
|
||||
.minus(expectedResults.percentageFees),
|
||||
oldBalances[this._takerAddress][makerAssetAddress]
|
||||
.plus(expectedResults.makerAssetFillAmount)
|
||||
.minus(expectedResults.percentageFees)
|
||||
.plus(expectedResults.maxOverboughtMakerAsset),
|
||||
.plus(makerAssetFillAmount)
|
||||
.minus(percentageFees)
|
||||
.plus(maxOverboughtMakerAsset),
|
||||
'Taker makerAsset balance',
|
||||
);
|
||||
expect(
|
||||
newBalances[this._orderFeeRecipientAddress][makerAssetAddress],
|
||||
'Order fee recipient makerAsset balance',
|
||||
).to.be.bignumber.equal(
|
||||
oldBalances[this._orderFeeRecipientAddress][makerAssetAddress].plus(expectedResults.percentageFees),
|
||||
);
|
||||
).to.be.bignumber.equal(oldBalances[this._orderFeeRecipientAddress][makerAssetAddress].plus(percentageFees));
|
||||
expect(
|
||||
newBalances[this._forwarderAddress][makerAssetAddress],
|
||||
'Forwarder contract makerAsset balance',
|
||||
@ -234,7 +250,7 @@ export class ForwarderTestFactory {
|
||||
expectedResults: ForwarderFillState,
|
||||
takerEthBalanceBefore: BigNumber,
|
||||
erc20Balances: ERC20BalancesByOwner,
|
||||
makerAssetContract: DummyERC20TokenContract | DummyERC721TokenContract,
|
||||
makerAssetContracts: Array<DummyERC20TokenContract | DummyERC721TokenContract>,
|
||||
options: {
|
||||
forwarderFeePercentage?: BigNumber;
|
||||
forwarderFeeRecipientEthBalanceBefore?: BigNumber;
|
||||
@ -277,12 +293,14 @@ export class ForwarderTestFactory {
|
||||
);
|
||||
}
|
||||
|
||||
for (const makerAssetContract of makerAssetContracts) {
|
||||
if (makerAssetContract instanceof DummyERC20TokenContract) {
|
||||
this._checkErc20Balances(erc20Balances, newBalances, expectedResults, makerAssetContract);
|
||||
} else if (options.makerAssetId !== undefined) {
|
||||
const newOwner = await makerAssetContract.ownerOf.callAsync(options.makerAssetId);
|
||||
expect(newOwner, 'New ERC721 owner').to.be.bignumber.equal(this._takerAddress);
|
||||
}
|
||||
}
|
||||
|
||||
expectBalanceWithin(
|
||||
newBalances[this._makerAddress][this._wethAddress],
|
||||
@ -313,18 +331,25 @@ export class ForwarderTestFactory {
|
||||
ordersInfoBefore: OrderInfo[],
|
||||
fractionalNumberOfOrdersToFill: number,
|
||||
): ForwarderFillState {
|
||||
const currentState = {
|
||||
const currentState: ForwarderFillState = {
|
||||
takerAssetFillAmount: constants.ZERO_AMOUNT,
|
||||
makerAssetFillAmount: constants.ZERO_AMOUNT,
|
||||
makerAssetFillAmount: {},
|
||||
protocolFees: constants.ZERO_AMOUNT,
|
||||
wethFees: constants.ZERO_AMOUNT,
|
||||
percentageFees: constants.ZERO_AMOUNT,
|
||||
percentageFees: {},
|
||||
maxOversoldWeth: constants.ZERO_AMOUNT,
|
||||
maxOverboughtMakerAsset: constants.ZERO_AMOUNT,
|
||||
};
|
||||
let remainingOrdersToFill = fractionalNumberOfOrdersToFill;
|
||||
|
||||
for (const [i, order] of orders.entries()) {
|
||||
if (currentState.makerAssetFillAmount[order.makerAssetData] === undefined) {
|
||||
currentState.makerAssetFillAmount[order.makerAssetData] = new BigNumber(0);
|
||||
}
|
||||
if (currentState.percentageFees[order.makerAssetData] === undefined) {
|
||||
currentState.percentageFees[order.makerAssetData] = new BigNumber(0);
|
||||
}
|
||||
|
||||
if (remainingOrdersToFill === 0) {
|
||||
break;
|
||||
}
|
||||
@ -365,7 +390,9 @@ export class ForwarderTestFactory {
|
||||
makerAssetAmount = BigNumber.max(makerAssetAmount.minus(makerAssetFilled), constants.ZERO_AMOUNT);
|
||||
|
||||
currentState.takerAssetFillAmount = currentState.takerAssetFillAmount.plus(takerAssetAmount);
|
||||
currentState.makerAssetFillAmount = currentState.makerAssetFillAmount.plus(makerAssetAmount);
|
||||
currentState.makerAssetFillAmount[order.makerAssetData] = currentState.makerAssetFillAmount[
|
||||
order.makerAssetData
|
||||
].plus(makerAssetAmount);
|
||||
|
||||
if (this._protocolFeeCollectorAddress !== constants.NULL_ADDRESS) {
|
||||
currentState.protocolFees = currentState.protocolFees.plus(
|
||||
@ -373,7 +400,9 @@ export class ForwarderTestFactory {
|
||||
);
|
||||
}
|
||||
if (order.takerFeeAssetData === order.makerAssetData) {
|
||||
currentState.percentageFees = currentState.percentageFees.plus(takerFee);
|
||||
currentState.percentageFees[order.makerAssetData] = currentState.percentageFees[
|
||||
order.makerAssetData
|
||||
].plus(takerFee);
|
||||
} else if (order.takerFeeAssetData === order.takerAssetData) {
|
||||
currentState.wethFees = currentState.wethFees.plus(takerFee);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user